Compare commits

...

351 Commits

Author SHA1 Message Date
Ivan Vorobei 47fd132451 Update SPStorkController.podspec 2019-07-05 22:42:45 +03:00
Ivan Vorobei f0c45211b2 Merge branch 'master' of https://github.com/IvanVorobei/SPStorkController 2019-07-05 22:42:15 +03:00
Ivan Vorobei 2952d5d559 Update example 2019-07-05 22:42:13 +03:00
Ivan Vorobei a6a5995402 Merge pull request #79 from dstranz/feature/improve_scrollview_support
Improve dismissing ScrollView
2019-07-05 22:39:57 +03:00
Dominique Stranz 8e476c702f Move whole UIScrollView to fix issue with floating header 2019-07-05 15:28:26 +02:00
Ivan Vorobei 5f7cc4c7b3 Merge branch 'master' of https://github.com/IvanVorobei/SPStorkController 2019-07-05 00:13:31 +03:00
Ivan Vorobei d8d4e1e339 Update README.md 2019-07-05 00:13:28 +03:00
Ivan Vorobei 0f78641fdb Merge pull request #78 from dstranz/feature/spm
Add Swift Package Manager
2019-07-03 12:48:13 +03:00
Dominique Stranz fd909a3ad6 Add SPM to README.md 2019-07-03 11:14:07 +02:00
Dominique Stranz 2bb0c190b3 Add tableView header position fix 2019-07-02 15:29:54 +02:00
Dominique Stranz 1ecc16c953 Add support for ViewController inside UINavigationController 2019-07-02 15:28:59 +02:00
Dominique Stranz 4698fbbe99 Add SPM support 2019-07-02 15:25:35 +02:00
Ivan Vorobei 03df02d214 Update UserInterfaceState.xcuserstate 2019-06-24 21:43:05 +03:00
Ivan Vorobei cbc46d4db4 Update to 1.6.6
Change layout system for arrow indicator. Now usage auto layout.
2019-06-19 07:41:18 +03:00
Ivan Vorobei 552f722a88 Update README.md 2019-06-18 17:54:16 +03:00
Ivan Vorobei 19caee5ccc Update README.md 2019-06-18 17:53:05 +03:00
Ivan Vorobei 2b5d20f26d Update README.md 2019-06-18 17:52:39 +03:00
Ivan Vorobei a6913a46a2 Add about Sheets 2019-06-18 17:51:41 +03:00
Ivan Vorobei fab0c7a274 Update README.md 2019-06-17 11:32:29 +03:00
Ivan Vorobei d04463f251 Update README.md 2019-06-17 11:13:26 +03:00
Ivan Vorobei d37aace4ec Update README.md 2019-06-17 11:11:48 +03:00
Ivan Vorobei 33d9b98d7d Update README.md 2019-06-17 11:10:46 +03:00
Ivan Vorobei 0363923e1b Update README.md 2019-06-17 11:09:37 +03:00
Ivan Vorobei 877108b08b Update README.md 2019-06-17 11:08:39 +03:00
Ivan Vorobei 7b25a6b878 Update README.md 2019-06-17 11:07:48 +03:00
Ivan Vorobei b69ae3ff7e Update README.md 2019-06-17 11:07:18 +03:00
Ivan Vorobei 4eac2276d4 Update README.md 2019-06-17 11:03:50 +03:00
Ivan Vorobei 38a6a10280 Update README.md 2019-06-17 11:02:12 +03:00
Ivan Vorobei aae897fc68 Update README.md 2019-06-17 11:00:27 +03:00
Ivan Vorobei 438811dbe5 Update README.md 2019-06-17 10:59:47 +03:00
Ivan Vorobei 7ea761b26e Update README.md 2019-06-16 23:20:30 +03:00
Ivan Vorobei 5c5457a09b Update README.md 2019-06-16 00:14:22 +03:00
Ivan Vorobei cdccba8929 Update README.md 2019-06-16 00:11:53 +03:00
Ivan Vorobei 196beb7592 Update README.md 2019-06-15 23:31:41 +03:00
Ivan Vorobei 91fe62e899 Update README.md 2019-06-15 23:30:40 +03:00
Ivan Vorobei bab71b560d Update README.md 2019-06-15 23:27:48 +03:00
Ivan Vorobei bb11bcd528 Add SPStorkSegue & icon for example 2019-06-15 23:25:30 +03:00
Ivan Vorobei d1f25b147f Update README.md 2019-06-15 14:25:50 +03:00
Ivan Vorobei 3eb459eab5 Update README.md 2019-06-15 14:22:50 +03:00
Ivan Vorobei 9060761733 Update README.md 2019-06-15 14:22:35 +03:00
Ivan Vorobei d7c5bd193c Update README.md 2019-06-15 14:20:16 +03:00
Ivan Vorobei 43a03de8d9 Update README.md 2019-06-15 14:19:46 +03:00
Ivan Vorobei 621bdb3e05 Update README.md 2019-06-15 14:19:18 +03:00
Ivan Vorobei d50ba88425 Update README.md 2019-06-15 14:19:00 +03:00
Ivan Vorobei 08b8422c26 Update README.md 2019-06-15 14:14:57 +03:00
Ivan Vorobei 9d13d508c6 Update scheme 2019-06-15 14:10:12 +03:00
Ivan Vorobei 6ae45f0568 Update README.md 2019-06-03 13:00:55 +03:00
Ivan Vorobei e5dbab65e1 Update README.md 2019-06-02 12:47:51 +03:00
Ivan Vorobei 07827b28a9 Update README.md 2019-06-02 12:21:24 +03:00
Ivan Vorobei 1dbd0ad54b Update README.md 2019-06-02 12:20:13 +03:00
Ivan Vorobei 49f2429633 Update README.md 2019-06-02 12:17:29 +03:00
Ivan Vorobei ef0a87e777 Update README.md 2019-06-02 11:59:53 +03:00
Ivan Vorobei b23a0b14e3 Update README.md 2019-06-02 11:57:05 +03:00
Ivan Vorobei a05b878af4 Update README.md 2019-06-02 11:56:40 +03:00
Ivan Vorobei 912c2d3903 Update README.md 2019-06-02 11:54:44 +03:00
Ivan Vorobei 3716912bc5 Update README.md 2019-06-02 11:53:51 +03:00
Ivan Vorobei ec9e174e14 Update README.md 2019-06-02 11:52:54 +03:00
Ivan Vorobei 6b98bf40c2 Update README.md 2019-06-02 11:51:51 +03:00
Ivan Vorobei 1167ca0a0d Update README.md 2019-06-02 11:50:46 +03:00
Ivan Vorobei c8c565d4ec Update README.md 2019-06-02 11:50:02 +03:00
Ivan Vorobei be0be7585b Update README.md 2019-06-02 11:46:52 +03:00
Ivan Vorobei c241629ea6 Update README.md 2019-06-02 11:44:35 +03:00
Ivan Vorobei 46afa63dbd Update README.md 2019-06-01 22:53:10 +03:00
Ivan Vorobei 682ab6127f Update README.md 2019-05-29 18:33:14 +03:00
Ivan Vorobei 4d70e9ca26 Update README.md 2019-05-29 18:31:56 +03:00
Ivan Vorobei 7091a625f3 Update README.md 2019-05-29 18:30:57 +03:00
Ivan Vorobei 5a83c3d1f3 Update README.md 2019-05-29 18:30:01 +03:00
Ivan Vorobei ad0c6eaf62 Update README.md 2019-05-25 15:34:29 +03:00
Ivan Vorobei 1f96f32980 Update README.md 2019-05-25 14:29:58 +03:00
Ivan Vorobei 03b38dfa0e Update README.md 2019-05-25 14:16:31 +03:00
Ivan Vorobei 2c35358336 Update README.md 2019-05-25 14:09:32 +03:00
Ivan Vorobei 18626df942 Update README.md 2019-05-25 14:09:13 +03:00
Ivan Vorobei 1a716980c0 Update README.md 2019-05-25 14:08:38 +03:00
Ivan Vorobei b5cf71046f Update FUNDING.yml 2019-05-24 22:00:23 +03:00
Ivan Vorobei 328d3739d6 Update README.md 2019-05-24 16:18:24 +03:00
Ivan Vorobei 2a858aa309 Update README.md 2019-05-24 16:17:33 +03:00
Ivan Vorobei f2937f6830 Update README.md 2019-05-24 12:57:20 +03:00
Ivan Vorobei c274892031 Update README.md 2019-05-24 12:57:03 +03:00
Ivan Vorobei 745968a053 Update README.md 2019-05-24 12:56:45 +03:00
Ivan Vorobei 4c90c708b7 Update README.md 2019-05-24 12:56:15 +03:00
Ivan Vorobei 871e96c1b9 Update README.md 2019-05-24 12:48:20 +03:00
Ivan Vorobei ad26638886 Update README.md 2019-05-24 12:48:01 +03:00
Ivan Vorobei 0306b46949 Update README.md 2019-05-24 12:47:34 +03:00
Ivan Vorobei 0f038463ed Update README.md 2019-05-24 12:46:56 +03:00
Ivan Vorobei ae01552b84 Update README.md 2019-05-24 12:46:37 +03:00
Ivan Vorobei 272bff5584 Update README.md 2019-05-24 12:45:50 +03:00
Ivan Vorobei 2adb5b4c88 Update README.md 2019-05-24 12:43:07 +03:00
Ivan Vorobei f930141a04 Update README.md 2019-05-24 12:42:50 +03:00
Ivan Vorobei 615fb4fcbc Update README.md 2019-05-24 12:42:15 +03:00
Ivan Vorobei 10dafb3853 Update README.md 2019-05-24 12:03:47 +03:00
Ivan Vorobei 7505c48033 Update README.md 2019-05-24 00:08:30 +03:00
Ivan Vorobei 35770b7362 Update README.md 2019-05-24 00:08:14 +03:00
Ivan Vorobei d478decba7 Update FUNDING.yml 2019-05-23 23:54:08 +03:00
Ivan Vorobei 51bb03327c Create FUNDING.yml 2019-05-23 23:27:59 +03:00
Ivan Vorobei f23eacf2ad Update README.md 2019-05-17 17:51:34 +03:00
Ivan Vorobei 04c9b77c75 Update README.md 2019-05-17 17:46:38 +03:00
Ivan Vorobei e7e83b5ad2 Update README.md 2019-05-17 17:46:01 +03:00
Ivan Vorobei 5911771ec9 Update README.md 2019-05-11 21:29:50 +03:00
Ivan Vorobei 6c1070848f Update README.md 2019-05-09 11:07:06 +03:00
Ivan Vorobei c18e8b8faf Update to 1.6.5
Fix bug with size `presentedView` when present other controller.
2019-05-08 18:14:35 +03:00
Ivan Vorobei 1d03424ab4 Update README.md 2019-05-08 11:20:34 +03:00
Ivan Vorobei 66556da572 Update README.md 2019-05-08 11:19:55 +03:00
Ivan Vorobei 6ebcfa59e9 Add Preview 2019-05-08 11:18:24 +03:00
Ivan Vorobei f6a2c31e2f Delete Preview.gif 2019-05-08 11:18:01 +03:00
Ivan Vorobei be3eb3f3a7 Update Preview 2019-05-08 11:16:57 +03:00
Ivan Vorobei a821389fee Update to 1.6.4
Update scale for parent controller. Update public method.
2019-05-07 13:43:50 +03:00
Ivan Vorobei e057aadaf1 Update public method 2019-05-07 13:43:05 +03:00
Ivan Vorobei 4c95e0ed3f Fix width
Reduce 1px of width parrent controller
2019-05-06 12:40:46 +03:00
Ivan Vorobei 71708b43c3 Update to 1.6.2
New width for parent controller.
2019-05-06 11:56:06 +03:00
Ivan Vorobei 66d7775d01 Update Example 2019-05-06 11:55:19 +03:00
Ivan Vorobei 298e0fdfc0 Update README.md 2019-05-05 18:04:59 +03:00
Ivan Vorobei 65070bce00 Update Example
And convert example project for swift 5 support.
2019-05-02 17:52:05 +03:00
Ivan Vorobei 2210afc7a7 Update README.md 2019-05-01 14:30:49 +03:00
Ivan Vorobei 94a8b06dde Update README.md 2019-04-30 20:58:58 +03:00
Ivan Vorobei 621e19cf78 Update README.md 2019-04-30 20:53:31 +03:00
Ivan Vorobei 6ae5e4f73d Update README.md 2019-04-30 16:31:35 +03:00
Ivan Vorobei 57d927bc49 Update README.md 2019-04-29 12:22:26 +03:00
Ivan Vorobei 1817c81bcc Update README.md 2019-04-29 12:20:49 +03:00
Ivan Vorobei 02e77c6074 Update README.md 2019-04-29 12:15:53 +03:00
Ivan Vorobei c922a8f522 Update README.md 2019-04-29 12:15:36 +03:00
Ivan Vorobei 86e7b3458d Update README.md 2019-04-29 12:15:00 +03:00
Ivan Vorobei a7ffa44434 Update README.md 2019-04-29 12:14:39 +03:00
Ivan Vorobei 96cc6b021a Update README.md 2019-04-29 12:14:16 +03:00
Ivan Vorobei 398da17b4a Update README.md 2019-04-28 23:05:55 +03:00
Ivan Vorobei 5e76f9f4d0 Update README.md 2019-04-28 18:52:54 +03:00
Ivan Vorobei a20a025a5b Update README.md 2019-04-28 18:47:39 +03:00
Ivan Vorobei 93e5b6f9de Update README.md 2019-04-28 18:46:27 +03:00
Ivan Vorobei 9d280b9b51 Update Readme 2019-04-28 16:28:53 +03:00
Ivan Vorobei 3d486c8f4b Update README.md 2019-04-26 18:39:12 +03:00
Ivan Vorobei d8fc226c9a Update README.md 2019-04-26 18:25:25 +03:00
Ivan Vorobei 7fd5eb41a6 Update Readme 2019-04-26 17:55:24 +03:00
Ivan Vorobei 263996e39d Update README.md 2019-04-26 17:53:52 +03:00
Ivan Vorobei b36cc7720d Update README.md 2019-04-26 17:53:38 +03:00
Ivan Vorobei f01ff0e904 Update Readme 2019-04-26 17:53:12 +03:00
Ivan Vorobei 92f29009c4 Update README.md 2019-04-26 17:45:06 +03:00
Ivan Vorobei b9813933b9 Update README.md 2019-04-26 17:36:54 +03:00
Ivan Vorobei ef0169a429 Update README.md 2019-04-26 17:35:52 +03:00
Ivan Vorobei 75766e6b7f Update README.md 2019-04-26 16:32:17 +03:00
Ivan Vorobei 9e8ed72013 Update README.md 2019-04-26 16:31:16 +03:00
Ivan Vorobei 29d6f3af93 Update README.md 2019-04-26 16:30:27 +03:00
Ivan Vorobei afa7e48e4c Update README.md 2019-04-26 16:20:25 +03:00
Ivan Vorobei b4c022e889 Update README.md 2019-04-26 16:13:36 +03:00
Ivan Vorobei 3c6e7e2c9c Update README.md 2019-04-26 15:55:05 +03:00
Ivan Vorobei 16e4685963 Update README.md 2019-04-26 15:53:29 +03:00
Ivan Vorobei e8e0e22259 Update README.md 2019-04-26 15:51:27 +03:00
Ivan Vorobei 19551282e6 Update Readme 2019-04-26 15:47:35 +03:00
Ivan Vorobei bc66345c6a Update Readme 2019-04-26 15:43:30 +03:00
Ivan Vorobei 73152ed8df Update README.md 2019-04-26 15:33:07 +03:00
Ivan Vorobei 431e9f58d5 Update README.md 2019-04-26 15:30:26 +03:00
Ivan Vorobei c3d75dac5e Update README.md 2019-04-26 15:30:01 +03:00
Ivan Vorobei cf9e7501d2 Update README.md 2019-04-26 15:27:47 +03:00
Ivan Vorobei 182ee5f0f0 Update README.md 2019-04-26 15:26:31 +03:00
Ivan Vorobei b2b37e717c Update README.md 2019-04-26 15:25:21 +03:00
Ivan Vorobei c0ea9d29a1 Update README.md 2019-04-26 15:21:57 +03:00
Ivan Vorobei cb55b4861a Update README.md 2019-04-26 15:20:57 +03:00
Ivan Vorobei 6a0aa725f7 Update README.md 2019-04-26 15:19:27 +03:00
Ivan Vorobei aebe62a9f2 Update README.md 2019-04-26 15:18:28 +03:00
Ivan Vorobei a0c0bf6885 Update README.md 2019-04-26 15:12:32 +03:00
Ivan Vorobei c0be873778 Update README.md 2019-04-26 15:11:04 +03:00
Ivan Vorobei 7fa550f79c Update README.md 2019-04-26 14:40:39 +03:00
Ivan Vorobei 6a22bb178b Update README.md 2019-04-25 16:57:19 +03:00
Ivan Vorobei 8993c2011d Update README.md 2019-04-24 12:03:09 +03:00
Ivan Vorobei 70199b65ee Update README.md 2019-04-23 12:40:06 +03:00
Ivan Vorobei 99ed2e84e1 Update README.md 2019-04-23 12:38:20 +03:00
Ivan Vorobei 6d8bf7c70a Update README.md 2019-04-23 12:26:31 +03:00
Ivan Vorobei a21dd49806 Update README.md 2019-04-23 12:05:22 +03:00
Ivan Vorobei 2cb195fcab Update README.md 2019-04-23 12:04:12 +03:00
Ivan Vorobei 4c78642040 Update README.md 2019-04-23 12:03:21 +03:00
Ivan Vorobei 73d297de19 Update README.md 2019-04-22 09:20:34 +03:00
Ivan Vorobei 1d167c9506 Update README.md 2019-04-22 09:19:19 +03:00
Ivan Vorobei 0b07e8e2cb Update README.md 2019-04-17 16:19:43 +03:00
Ivan Vorobei 8500571554 Update README.md 2019-04-17 16:11:08 +03:00
Ivan Vorobei 13134f2748 Update README.md 2019-04-17 16:05:39 +03:00
Ivan Vorobei 6e1a5c85b3 Update README.md 2019-04-17 16:04:01 +03:00
Ivan Vorobei eab9045cb5 Update README.md 2019-04-17 16:03:25 +03:00
Ivan Vorobei f567582b4d Update README.md 2019-04-17 16:00:48 +03:00
Ivan Vorobei 30365fde24 Fix protect level
Fix bug with `topScrollIndicatorInset` property, set `public` protect level. Add support in `podspec` swift `4.2` & `5.0`.
2019-04-17 00:49:40 +03:00
Ivan Vorobei 6b8fa51706 Update to 1.6.1
Add haptic feedback for some moments: see `hapticMoments`. Update example and Readme. Add support Carthage. Fix bug with func `removeAllAnimation` for `IndicatorView`.
2019-04-12 14:05:27 +03:00
Ivan Vorobei 1f1aa2ea8d Fix edit action
If translation < -50, indicator now force hide. Fix bug with edit actions for table view: implement `gestureRecognizerShouldBegin`.
2019-04-08 11:09:18 +03:00
Ivan Vorobei 914ce0f689 Fix bug with gester and EditingStyle for TableView
Implement `gestureRecognizerShouldBegin` func.
2019-04-05 23:40:00 +03:00
Ivan Vorobei 5dfab81504 Update SPStorkController.podspec 2019-04-04 14:10:29 +03:00
Ivan Vorobei 9270185bf6 Update SPStorkController.podspec 2019-04-04 14:06:45 +03:00
Ivan Vorobei 43bc6c27ee Update SPStorkController.podspec 2019-03-26 14:51:25 +03:00
Ivan Vorobei 551ccf3bf1 Update SPStorkController.podspec 2019-03-26 14:40:17 +03:00
Ivan Vorobei 0488521869 Update SPStorkController.podspec 2019-03-26 14:32:46 +03:00
Ivan Vorobei 9eae918f9d Update SPStorkController.podspec 2019-03-26 14:32:13 +03:00
Ivan Vorobei 28ed6b6ebc Update SPStorkController.podspec 2019-03-26 14:30:08 +03:00
Ivan Vorobei b783329b69 Update SPStorkController.podspec 2019-03-26 14:26:36 +03:00
Ivan Vorobei 84256152dc Update to 1.6
Update duration animation for show / hide indicator view when scrolling.
2019-03-26 14:19:27 +03:00
Ivan Vorobei ac366c84fd Update to 1.5.8 2019-03-25 23:30:30 +03:00
Ivan Vorobei 64bebe0645 Update to 1.5.7
Add `closeButton` and parametres.
2019-03-25 15:42:10 +03:00
Ivan Vorobei 1efa55aefb Update to 1.5.6
Add events delegate
2019-03-24 18:57:27 +03:00
Ivan Vorobei e5371ca20c Update to 1.5.5
Add new parameter `hideIndicatorWhenScroll` - it allow shows and hide indicator when scrolling. Also fixed bug when invalid frame for dismissing action.
2019-03-24 13:23:08 +03:00
Ivan Vorobei fdbc1e3527 Update README.md 2019-03-23 23:28:22 +03:00
Ivan Vorobei 306547671c Update README.md 2019-03-23 19:46:22 +03:00
Ivan Vorobei 1fede9eb64 Update to 1.5.4
Fix bug with dublicate indicator
2019-03-07 11:34:01 +03:00
Ivan Vorobei 3ef2ff81bc Update README.md 2019-03-04 15:34:07 +03:00
Ivan Vorobei 83330bb509 Update to 1.5.2 2019-03-03 18:48:30 +03:00
Ivan Vorobei c4da6ff27e Update to 1.5.1
Add `cornerRadius` property.
2019-03-01 16:54:54 +03:00
Ivan Vorobei 686bbd749b Add scroll indicator inset 2019-02-24 16:42:54 +03:00
Ivan Vorobei 7b151fd268 Update to 1.5
Change interactive parapetres. Fix logic for dismiss controller by drag down on `UIScrollView`
2019-02-20 01:23:55 +03:00
Ivan Vorobei 7bdf702703 Update README.md 2019-02-19 20:07:37 +03:00
Ivan Vorobei 0d1381d941 Update to 1.4.8
Change logic for dismiss controller when scroll down in `UIScrollView`.
2019-02-19 20:01:53 +03:00
Ivan Vorobei 39c30a030d Update SPStorkController.podspec 2019-02-19 12:31:58 +03:00
Ivan Vorobei 9e4f6e086f Update to 1.4.6
Added dismiss trigger when scrollView is scrolling down. Also added `swipeToDismissEnabled` checking to allow scrollView bounce when `swipeToDismissEnabled` is disabled.
2019-02-19 12:17:46 +03:00
Ivan Vorobei 5bc31cdc57 Merge pull request #37 from ilia3546/master
Added dismiss trigger when scrollView is scrolling down. Also added `swipeToDismissEnabled` checking to allow scrollView bounce when `swipeToDismissEnabled` is disabled.
2019-02-19 12:05:30 +03:00
Ilya Kharlamov 7d53ed31e6 Fix 2019-02-18 14:26:57 +03:00
Ilya Kharlamov 80806de694 Fix 2019-02-18 14:20:58 +03:00
Ilya Kharlamov 8d8458cc34 Revert "Custom width;"
This reverts commit dd2b29f27a.
2019-02-18 13:58:19 +03:00
Ilya Kharlamov 6e09df910e Custom width;
Ability to disable snapshot scaling;
2019-02-18 13:50:00 +03:00
Ilya Kharlamov 253f60cfa9 ScrollView with disabled swipeToDismissEnabled fixed;
Added trigger for dismissing when view is scrolling down;
2019-02-18 13:10:29 +03:00
Ivan Vorobei 81ed7b01cd Fix some bugs
Fix bug when present 2 or more controllers with custom heights. Also fix bug with grade for `presentingController` view.
2019-02-14 00:23:35 +03:00
Ivan Vorobei d0f120d49c Remove old code 2019-02-09 15:04:10 +03:00
Ivan Vorobei bc3555e715 Update README.md 2019-02-09 09:50:20 +03:00
Ivan Vorobei 432a1206cf Update README.md 2019-02-09 09:48:04 +03:00
Ivan Vorobei 488b19fc46 Update README.md 2019-02-09 09:29:22 +03:00
Ivan Vorobei edc2b6ea41 Fix example of table view 2019-02-09 09:27:13 +03:00
Ivan Vorobei 79d64599d0 Update SPStorkController.podspec 2019-02-08 19:02:59 +03:00
Ivan Vorobei 854ab2aef2 Fix bug
for update translation for scroll when presentng controller
2019-02-08 19:00:17 +03:00
Ivan Vorobei 04c4929e7d Update Readme.md 2019-02-08 18:20:47 +03:00
Ivan Vorobei 6e4e9713c3 Update example 2019-02-07 23:06:41 +03:00
Ivan Vorobei e37c43c8f3 Update SPStorkController.podspec 2019-02-07 11:45:07 +03:00
Ivan Vorobei 001536c835 Fix presented and dismiss action
Now if dismiss controller - keyboard will hide without delay. Also for func `presentAsStork` added property `height`. For SPStorkPresentingAnimationController fix start sizes
2019-02-06 10:53:58 +03:00
Ivan Vorobei 5a7d01e1e4 Update to 1.4.2
Add parameter `translateForDismiss`, which customise translation for dismiss controller. Default is `240`. Rename some parametrs.
2019-02-05 09:45:53 +03:00
Ivan Vorobei a1d17d994c Update to 1.4
If tap on `indicatorView`, controller will be close as in app Apple Music by Apple
2019-02-04 20:41:20 +03:00
Ivan Vorobei f56a054b91 Update SPStorkController.podspec 2019-02-04 01:41:40 +03:00
Ivan Vorobei bd26c4d721 Update SPStorkController.podspec 2019-02-04 01:39:58 +03:00
Ivan Vorobei 6927d90572 Update SPStorkController.podspec 2019-02-04 01:37:03 +03:00
Ivan Vorobei ea32992f63 Update to 1.2.8 2019-02-04 01:34:25 +03:00
Ivan Vorobei 05ba026c1f Update to 1.2.6
Add extenshion to UIViewController. Property `isPresentedAsStork` check if currenct controller present with this pod. For simple usage add func `presentAsStork` - need pass controller only.
2019-02-04 01:20:29 +03:00
Ivan Vorobei b3f49a2a94 Update SPStorkController.podspec 2019-02-01 03:15:56 +03:00
Ivan Vorobei c3aa4dc17e Update SPStorkController.podspec 2019-01-31 22:07:36 +03:00
Ivan Vorobei d9f67b57f8 Update SPStorkController.podspec 2019-01-31 21:49:03 +03:00
Ivan Vorobei 962d1a937d Update to 1.2.2
Add new parametr - `colorIndicator` for change color arrow. Update example & Readme. Fix `SPStorkIndicatorView `
2019-01-31 21:20:53 +03:00
Ivan Vorobei bc6c7ff45e Update README.md 2019-01-31 08:06:47 +03:00
Ivan Vorobei ec7abd4a2a Update example
Update `SparrowKit` pod
2019-01-26 14:01:36 +03:00
Ivan Vorobei 6904e0916a Update UserInterfaceState.xcuserstate 2019-01-21 16:33:48 +03:00
Ivan Vorobei 90ee3d35da Add navigation controller for example 2019-01-21 15:15:54 +03:00
Ivan Vorobei 7f998787a4 Update to 1.2.1 version
Now if set custom height more default value, it aotu change to default value. Update example and pod version.
2019-01-18 13:51:49 +03:00
Ivan Vorobei 618da7d9ea Update README.md 2019-01-18 11:58:08 +03:00
Ivan Vorobei f062d07f94 Add Images 2019-01-15 20:31:03 +03:00
Ivan Vorobei 926e1f643c Add Images 2019-01-15 20:28:19 +03:00
Ivan Vorobei cdd8f4a2f4 Remove images 2019-01-13 14:28:54 +03:00
Ivan Vorobei 30cf10c595 Update README.md 2019-01-13 14:28:10 +03:00
Ivan Vorobei 884791b05c Create youtube3.jpg 2019-01-13 14:27:50 +03:00
Ivan Vorobei 4d0530e0a1 Update README.md 2019-01-13 14:25:42 +03:00
Ivan Vorobei 48741988b2 img 2019-01-13 14:25:20 +03:00
Ivan Vorobei 6297d5a5db Update youtube.jpg 2019-01-13 14:24:45 +03:00
Ivan Vorobei 3f60e48547 Update README.md 2019-01-13 14:23:44 +03:00
Ivan Vorobei 2ffe86afb9 Update youtube.jpg 2019-01-13 14:23:12 +03:00
Ivan Vorobei cf5bf12513 Update README.md 2019-01-13 14:18:03 +03:00
Ivan Vorobei 96aa1a6064 Create youtube.jpg 2019-01-13 14:15:38 +03:00
Ivan Vorobei 3241abd195 Delete SPStorkController-YouTube.jpg 2019-01-02 12:08:27 +03:00
Ivan Vorobei b72f10c7bd Update README.md 2019-01-02 12:08:06 +03:00
Ivan Vorobei 40bcff3cb6 Create SPStorkController-YouTube.jpg 2019-01-02 12:07:19 +03:00
Ivan Vorobei 01cc226253 Update README.md 2019-01-01 22:51:43 +03:00
Ivan Vorobei 691045d012 Update README.md 2019-01-01 22:49:49 +03:00
Ivan Vorobei 59f52f258a Update README.md 2018-12-31 18:50:40 +03:00
Ivan Vorobei bfece3194a Update README.md 2018-12-31 18:49:18 +03:00
Ivan Vorobei 6cccf24ffa Update README.md 2018-12-31 18:47:17 +03:00
Ivan Vorobei 635878e456 Update README.md 2018-12-30 18:55:52 +03:00
Ivan Vorobei d839e0f414 Update README.md 2018-12-30 18:54:01 +03:00
Ivan Vorobei e3cbc318be Update README.md 2018-12-30 18:51:38 +03:00
Ivan Vorobei e4232ec045 Update README.md 2018-12-29 17:31:29 +03:00
Ivan Vorobei 02346c0814 Update README.md 2018-12-28 21:55:13 +03:00
Ivan Vorobei 0b98593954 Update README.md 2018-12-28 21:54:36 +03:00
Ivan Vorobei 518700f9cd Delete spstork-preivew.jpg 2018-12-28 20:53:53 +03:00
Ivan Vorobei 5e02985ef2 Update README.md 2018-12-28 20:33:00 +03:00
Ivan Vorobei 539589e029 Update README.md 2018-12-28 19:50:04 +03:00
Ivan Vorobei 3434c487cd Update README.md 2018-12-28 19:48:53 +03:00
Ivan Vorobei c18e72add2 Update README.md 2018-12-28 19:46:59 +03:00
Ivan Vorobei c5e407fd80 Update README.md 2018-12-28 19:46:16 +03:00
Ivan Vorobei 3937c9caa5 Update README.md 2018-12-28 19:45:34 +03:00
Ivan Vorobei 38332bed39 Update README.md 2018-12-28 19:44:41 +03:00
Ivan Vorobei 40c7eee5de Update README.md 2018-12-28 19:43:48 +03:00
Ivan Vorobei 2e881179ee Update README.md 2018-12-28 19:42:10 +03:00
Ivan Vorobei 749640d359 Add video tutorial 2018-12-28 19:41:29 +03:00
Ivan Vorobei fd68fcb9f5 Update Readme 2018-12-28 18:44:07 +03:00
Ivan Vorobei 473f8ae0f1 Update README.md 2018-12-28 18:43:08 +03:00
Ivan Vorobei 75825b5e0a Update README 2018-12-28 18:42:17 +03:00
Ivan Vorobei f1e053d0d3 Update UserInterfaceState.xcuserstate 2018-12-24 16:49:58 +03:00
Ivan Vorobei 6208c93d40 Update README.md 2018-12-18 21:16:13 +03:00
Ivan Vorobei 8c64cd09da Update README.md 2018-12-18 16:32:23 +03:00
Ivan Vorobei c36078cda2 Update to 1.2
- After tap parrent controller auto dismiss
- Update example
2018-12-17 21:47:26 +03:00
Ivan Vorobei b78c82194f Update README.md 2018-12-17 20:02:13 +03:00
Ivan Vorobei a112473a04 Update Readme.md 2018-12-17 13:40:22 +03:00
Ivan Vorobei c1b135a30a Add example of TableView for SPStorkController
- Upadte example
- Update readme
- Add example table view for SPStorkController
2018-12-14 12:46:33 +03:00
Ivan Vorobei c1a7622a26 Update README.md 2018-12-13 23:29:42 +03:00
Ivan Vorobei c3421432b0 Update README.md 2018-12-13 19:58:12 +03:00
Ivan Vorobei 99353d3610 Update README.md 2018-12-12 17:01:17 +03:00
Ivan Vorobei 3bee6898e0 Update README.md 2018-12-12 16:54:24 +03:00
Ivan Vorobei 587790b1db Merge pull request #11 from jobinsjohn/patch-3
Updated ReadMe
2018-12-12 16:53:21 +03:00
Ivan Vorobei 2698cfe23d Merge pull request #12 from jobinsjohn/patch-4
Updated ReadMe
2018-12-12 16:52:48 +03:00
Jobins John 42b82886ca Updated ReadMe
Fixed typo in read me file
2018-12-12 17:44:46 +04:00
Jobins John 7af77b2596 Updated ReadMe file
Fixed typos
2018-12-12 17:43:36 +04:00
Ivan Vorobei 7c663e7760 Update README.md 2018-12-12 16:36:08 +03:00
Ivan Vorobei f561e94110 Update README.md 2018-12-12 16:35:47 +03:00
Ivan Vorobei 3bf06ae2b3 Update README.md 2018-12-12 16:35:27 +03:00
Ivan Vorobei a6d5e6f97d Merge pull request #10 from jobinsjohn/patch-2
Updated ReadMe
2018-12-12 16:34:35 +03:00
Ivan Vorobei 0f20afb87c Merge pull request #9 from jobinsjohn/patch-1
Updated ReadMe
2018-12-12 16:34:13 +03:00
Jobins John 2368747150 Updated Readme File
Added shields in read me for platform
2018-12-12 17:29:21 +04:00
Jobins John 32a010e1ca Updated ReadMe file
fixed typos and grammar mistakes
2018-12-12 17:23:49 +04:00
Ivan Vorobei fce0efaffa Update example 2018-12-12 16:10:45 +03:00
Ivan Vorobei 1500c272b1 Update to 1.1.8 version
- Fix bugs with paramtrs
- Add paramtr custom height
- Update Readme
2018-12-12 16:04:25 +03:00
Ivan Vorobei ba859e3646 Update README.md 2018-12-12 12:47:52 +03:00
Ivan Vorobei 01b8702c4a Update README.md 2018-12-12 12:46:22 +03:00
Ivan Vorobei ec809ee1c5 Update Readme.md 2018-12-12 12:28:53 +03:00
Ivan Vorobei a5f41ab3d6 Update gif 2018-12-12 12:27:47 +03:00
Ivan Vorobei 58554f1449 Update README.md 2018-12-12 11:43:18 +03:00
Ivan Vorobei 8c1b6e2c8d Add custom height 2018-12-10 21:39:48 +03:00
Ivan Vorobei 724635e387 Update README.md 2018-12-10 21:36:35 +03:00
Ivan Vorobei dfc0fb8e52 Update gif 2018-12-10 21:34:47 +03:00
Ivan Vorobei 50c9ab4104 Create gif-mockup.gif 2018-12-10 21:33:23 +03:00
Ivan Vorobei bb0821d2cc Update README.md 2018-12-07 13:45:03 +03:00
Ivan Vorobei ae29eba5b9 Update README.md 2018-12-07 13:44:30 +03:00
Ivan Vorobei 0c98c3c699 Update to 1.1.6 2018-12-07 13:40:49 +03:00
Ivan Vorobei 4ecc8e96d8 Update Readme.md 2018-12-07 13:40:13 +03:00
Ivan Vorobei fac454f41d remove source 2018-12-07 13:14:31 +03:00
Ivan Vorobei ae68a1be5d Update README.md 2018-12-06 17:33:44 +03:00
Ivan Vorobei c111a20c86 Update README.md 2018-12-06 17:28:05 +03:00
Ivan Vorobei cc1b1ac604 Update SPStorkPresentationController.swift 2018-12-06 17:14:37 +03:00
Ivan Vorobei 265f77dd5e Update README.md 2018-12-06 15:35:02 +03:00
Ivan Vorobei aacf153d0e Update README.md 2018-12-06 15:26:51 +03:00
Ivan Vorobei 5b8fdf6590 Update to 1.1.1 2018-12-06 15:21:50 +03:00
Ivan Vorobei 883ade3052 Update README.md 2018-12-06 15:08:41 +03:00
Ivan Vorobei 44837098ea Update to version 1.1 2018-12-06 15:05:47 +03:00
Ivan Vorobei 42063749ed Update README.md 2018-12-04 12:20:52 +03:00
Ivan Vorobei 464df2eb5c Update README.md 2018-12-04 12:19:31 +03:00
Ivan Vorobei 6f48df5761 Update README.md 2018-12-04 12:10:54 +03:00
Ivan Vorobei e331793afc Update README.md 2018-12-04 12:10:23 +03:00
Ivan Vorobei 82152b698a Update README.md 2018-12-03 14:33:11 +03:00
Ivan Vorobei 75eaaec598 Update Readme 2018-12-03 10:53:25 +03:00
Ivan Vorobei d2cc2e424c Update README.md 2018-12-02 21:13:33 +03:00
Ivan Vorobei 0ac35b4494 Update README.md 2018-12-02 21:13:18 +03:00
Ivan Vorobei 6c2600dea8 Update README.md 2018-12-01 23:36:18 +03:00
Ivan Vorobei 26d7422216 Update README.md 2018-12-01 23:35:01 +03:00
Ivan Vorobei a4a02b598f Update README.md 2018-11-30 20:54:40 +03:00
Ivan Vorobei 0309bb2e0e Update example and Readme 2018-11-30 20:43:35 +03:00
Ivan Vorobei 4e0507f132 Update README.md 2018-11-30 20:33:04 +03:00
Ivan Vorobei 8e33ff7614 Add donate banner 2018-11-30 20:32:36 +03:00
Ivan Vorobei b7ab9e0327 Update README.md 2018-11-30 20:30:39 +03:00
Ivan Vorobei 4129f23d00 Add header banner 2018-11-30 20:29:57 +03:00
Ivan Vorobei 91b01d83d9 Update Readme 2018-11-30 20:26:33 +03:00
Ivan Vorobei 4eda691bd0 Update README.md 2018-11-30 20:25:06 +03:00
Ivan Vorobei 0889352299 Update Readme 2018-11-30 20:18:38 +03:00
Ivan Vorobei 34f267d43c Update SPStorkController.podspec 2018-11-29 12:55:29 +03:00
Ivan Vorobei 425f8ec1ca Fix layout for SPStorkController
- Fix layout for SPStorkController
- Add extenshion for `CGRect` static var `displayFrame`. It is rect for screen size
- Set clear background color for `SPStorkIndicatorView`
2018-11-29 12:49:56 +03:00
Ivan Vorobei 25ea7eab4c Add more interactive to SPStorkController 2018-11-29 11:10:18 +03:00
Ivan Vorobei 16f060820f Update README.md 2018-11-28 20:11:41 +03:00
Ivan Vorobei 4c8faf7695 Update README.md 2018-11-28 17:49:37 +03:00
Ivan Vorobei 5b843582d4 Update README.md 2018-11-28 17:30:29 +03:00
225 changed files with 8024 additions and 7102 deletions
+8
View File
@@ -0,0 +1,8 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: ivanvorobei
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: # https://xcode-shop.com
+37
View File
@@ -1 +1,38 @@
# Mac OS X
.DS_Store
# Xcode
## Build generated
build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
.build/
# Carthage
Carthage/Build
File diff suppressed because it is too large Load Diff
+9 -21
View File
@@ -7,31 +7,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let navigationController = UINavigationController(rootViewController: Controller())
self.launch(rootViewController: navigationController)
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
func launch(rootViewController: UIViewController) {
let frame = UIScreen.main.bounds
self.window = UIWindow(frame: frame)
self.window!.rootViewController = rootViewController
self.window!.makeKeyAndVisible()
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
@@ -1,93 +1,111 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon_20pt@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon_20pt@3x.png",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon_29pt@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon_29pt@3x.png",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon_40pt@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon_40pt@3x.png",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon_60pt@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon_60pt@3x.png",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon_20pt.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon_20pt@2x-1.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon_29pt.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon_29pt@2x-1.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon_40pt.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon_40pt@2x-1.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon_76pt.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon_76pt@2x.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "icon_83.5@2x.png",
"scale" : "2x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon.png",
"scale" : "1x"
}
],
Binary file not shown.

After

Width:  |  Height:  |  Size: 483 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
+43
View File
@@ -0,0 +1,43 @@
import UIKit
class Controller: UIViewController {
var presentControllerButton = UIButton.init(type: UIButton.ButtonType.system)
var presentTableControllerButton = UIButton.init(type: UIButton.ButtonType.system)
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.presentControllerButton.setTitle("Show ViewController", for: .normal)
self.presentControllerButton.addTarget(self, action: #selector(self.presentModalViewController), for: .touchUpInside)
self.presentControllerButton.sizeToFit()
self.presentControllerButton.center.x = self.view.frame.width / 2
self.presentControllerButton.center.y = self.view.frame.height / 4 * 3
self.view.addSubview(self.presentControllerButton)
self.presentTableControllerButton.setTitle("Show TableController", for: .normal)
self.presentTableControllerButton.addTarget(self, action: #selector(self.presentModalTableViewController), for: .touchUpInside)
self.presentTableControllerButton.sizeToFit()
self.presentTableControllerButton.center.x = self.view.frame.width / 2
self.presentTableControllerButton.frame.origin.y = self.presentControllerButton.frame.bottomY + 10
self.view.addSubview(self.presentTableControllerButton)
}
@objc func presentModalViewController() {
let modal = ModalViewController()
let transitionDelegate = SPStorkTransitioningDelegate()
modal.transitioningDelegate = transitionDelegate
modal.modalPresentationStyle = .custom
self.present(modal, animated: true, completion: nil)
}
@objc func presentModalTableViewController() {
let modal = ModalTableViewController()
let transitionDelegate = SPStorkTransitioningDelegate()
modal.transitioningDelegate = transitionDelegate
modal.modalPresentationStyle = .custom
self.present(modal, animated: true, completion: nil)
}
}
@@ -21,7 +21,10 @@
import UIKit
public struct SPStyleKit {
public enum SPFakeBarNavigationStyle {
private init() {}
case large
case small
case stork
case noContent
}
@@ -0,0 +1,250 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
open class SPFakeBarView: UIView {
public var style: SPFakeBarNavigationStyle = .small {
didSet {
self.updateStyle()
}
}
private var settedHeight: CGFloat = 0
public var height: CGFloat {
get {
return (self.settedHeight) + (self.addStatusBarHeight ? UIApplication.shared.statusBarFrame.height : 0)
}
set {
self.settedHeight = newValue
self.updateHeight()
}
}
public var addStatusBarHeight: Bool = true {
didSet {
self.updateHeight()
}
}
public var elementsColor: UIColor = SPFakeBarView.navigationElementsColor {
didSet {
self.leftButton.setTitleColor(self.elementsColor, for: .normal)
self.leftButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
self.rightButton.setTitleColor(self.elementsColor, for: .normal)
self.rightButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
}
}
public var closeButtonPossition: CloseButtonPosition = .none {
didSet {
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
switch self.closeButtonPossition {
case .left:
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
case .right:
self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
case .none:
break
}
}
}
public var titleLabel = UILabel.init()
public var subtitleLabel = UILabel.init()
public var leftButton = UIButton.init()
public var rightButton = UIButton.init()
public let separatorView = UIView()
public let blurView: UIVisualEffectView = {
let effect = UIBlurEffect(style: .extraLight)
return UIVisualEffectView.init(effect: effect)
}()
private var titleBottomConstraint: NSLayoutConstraint?
private var heightConstraint: NSLayoutConstraint?
private var topConstraint: NSLayoutConstraint?
private var leadingConstraint: NSLayoutConstraint?
private var trailingConstraint: NSLayoutConstraint?
public init(style: SPFakeBarNavigationStyle) {
super.init(frame: CGRect.zero)
self.style = style
self.commonInit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.style = .small
self.commonInit()
}
private func commonInit() {
self.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.blurView)
self.blurView.translatesAutoresizingMaskIntoConstraints = false
self.blurView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
self.blurView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.blurView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
self.blurView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
self.addSubview(self.separatorView)
self.separatorView.backgroundColor = UIColor.init(red: 191 / 255.0, green: 191 / 255.0, blue: 191 / 255.0, alpha: 1)
self.separatorView.translatesAutoresizingMaskIntoConstraints = false
self.separatorView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
self.separatorView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.separatorView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
self.separatorView.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
self.addSubview(self.titleLabel)
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
self.titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
self.titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16).isActive = true
self.titleBottomConstraint = self.titleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12)
self.titleBottomConstraint?.isActive = true
self.addSubview(self.subtitleLabel)
self.subtitleLabel.textColor = UIColor.init(red: 142 / 255.0, green: 142 / 255.0, blue: 146 / 255.0, alpha: 1)
self.subtitleLabel.font = UIFont.systemFont(ofSize: 13, weight: .semibold)
self.subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
self.subtitleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
self.subtitleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16).isActive = true
self.subtitleLabel.bottomAnchor.constraint(equalTo: self.titleLabel.topAnchor, constant: 0).isActive = true
self.leftButton.setTitleColor(self.elementsColor, for: .normal)
self.leftButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
self.leftButton.titleLabel?.textAlignment = .left
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
self.leftButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 17, bottom: 0, right: 0)
self.addSubview(self.leftButton)
self.leftButton.translatesAutoresizingMaskIntoConstraints = false
self.leftButton.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
self.leftButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12).isActive = true
self.rightButton.setTitleColor(self.elementsColor, for: .normal)
self.rightButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
self.rightButton.titleLabel?.textAlignment = .right
self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
self.rightButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16)
self.addSubview(self.rightButton)
self.rightButton.translatesAutoresizingMaskIntoConstraints = false
self.rightButton.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.rightButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12).isActive = true
self.closeButtonPossition = .none
self.setContraints()
self.updateStyle()
}
override open func layoutSubviews() {
super.layoutSubviews()
self.setContraints()
}
private func setContraints() {
if let superview = self.superview {
if self.topConstraint == nil {
self.topConstraint = self.topAnchor.constraint(equalTo: superview.topAnchor)
self.topConstraint?.isActive = true
self.leadingConstraint = self.leadingAnchor.constraint(equalTo: superview.leadingAnchor)
self.leadingConstraint?.isActive = true
self.trailingConstraint = self.trailingAnchor.constraint(equalTo: superview.trailingAnchor)
self.trailingConstraint?.isActive = true
self.heightConstraint = self.heightAnchor.constraint(equalToConstant: self.height)
self.heightConstraint?.isActive = true
self.updateHeight()
}
}
}
private func updateStyle() {
switch self.style {
case .small:
if UIApplication.shared.statusBarFrame.height == 44 {
self.height = 88 - 44
self.titleBottomConstraint?.constant = -12
} else {
self.height = 64 - 20
self.titleBottomConstraint?.constant = -12
}
self.addStatusBarHeight = true
self.titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
self.titleLabel.textAlignment = .center
case .stork:
self.height = 66
self.titleBottomConstraint?.constant = -12
self.addStatusBarHeight = false
self.titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
self.titleLabel.textAlignment = .center
case .large:
if UIApplication.shared.statusBarFrame.height == 44 {
self.height = 140 - 44
self.titleBottomConstraint?.constant = -8
} else {
self.height = 116 - 20
self.titleBottomConstraint?.constant = -4
}
self.addStatusBarHeight = true
self.titleLabel.font = UIFont.systemFont(ofSize: 34, weight: .bold)
self.titleLabel.textAlignment = .left
break
case .noContent:
self.height = 0
self.addStatusBarHeight = true
}
self.updateConstraints()
}
private func updateHeight() {
self.heightConstraint?.constant = self.height
self.updateConstraints()
}
public enum CloseButtonPosition {
case left
case right
case none
}
}
extension SPFakeBarView {
static var navigationElementsColor: UIColor {
get {
if UINavigationBar.appearance().tintColor != nil {
return UINavigationBar.appearance().tintColor
} else {
return UIColor.init(red: 0 / 255.0, green: 122 / 255.0, blue: 255 / 255.0, alpha: 1)
}
}
set {
UINavigationBar.appearance().tintColor = newValue
}
}
}
@@ -0,0 +1,88 @@
import UIKit
class SPStorkCodeDraw : NSObject {
private struct Cache {
static let gradient: CGGradient = CGGradient(colorsSpace: nil, colors: [UIColor.red.cgColor, UIColor.red.cgColor] as CFArray, locations: [0, 1])!
}
@objc dynamic class var gradient: CGGradient { return Cache.gradient }
@objc dynamic class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 100, height: 100), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 100, y: resizedFrame.height / 100)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 92.02, y: 22.92))
bezierPath.addLine(to: CGPoint(x: 64.42, y: 50.52))
bezierPath.addLine(to: CGPoint(x: 92.02, y: 78.13))
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 92.99), controlPoint1: CGPoint(x: 96.13, y: 82.23), controlPoint2: CGPoint(x: 96.13, y: 88.89))
bezierPath.addCurve(to: CGPoint(x: 84.59, y: 96.07), controlPoint1: CGPoint(x: 89.97, y: 95.05), controlPoint2: CGPoint(x: 87.28, y: 96.07))
bezierPath.addCurve(to: CGPoint(x: 77.16, y: 92.99), controlPoint1: CGPoint(x: 81.9, y: 96.07), controlPoint2: CGPoint(x: 79.22, y: 95.05))
bezierPath.addLine(to: CGPoint(x: 49.55, y: 65.38))
bezierPath.addLine(to: CGPoint(x: 21.95, y: 92.99))
bezierPath.addCurve(to: CGPoint(x: 14.51, y: 96.07), controlPoint1: CGPoint(x: 19.89, y: 95.05), controlPoint2: CGPoint(x: 17.2, y: 96.07))
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 92.99), controlPoint1: CGPoint(x: 11.82, y: 96.07), controlPoint2: CGPoint(x: 9.13, y: 95.05))
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 78.13), controlPoint1: CGPoint(x: 2.97, y: 88.89), controlPoint2: CGPoint(x: 2.97, y: 82.23))
bezierPath.addLine(to: CGPoint(x: 34.69, y: 50.52))
bezierPath.addLine(to: CGPoint(x: 7.08, y: 22.92))
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 8.04), controlPoint1: CGPoint(x: 2.97, y: 18.8), controlPoint2: CGPoint(x: 2.97, y: 12.15))
bezierPath.addCurve(to: CGPoint(x: 21.94, y: 8.04), controlPoint1: CGPoint(x: 11.18, y: 3.94), controlPoint2: CGPoint(x: 17.84, y: 3.94))
bezierPath.addLine(to: CGPoint(x: 49.55, y: 35.65))
bezierPath.addLine(to: CGPoint(x: 77.16, y: 8.04))
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 8.04), controlPoint1: CGPoint(x: 81.26, y: 3.94), controlPoint2: CGPoint(x: 87.92, y: 3.94))
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 22.92), controlPoint1: CGPoint(x: 96.13, y: 12.15), controlPoint2: CGPoint(x: 96.13, y: 18.8))
bezierPath.close()
color.setFill()
bezierPath.fill()
context.restoreGState()
}
@objc(StyleKitNameResizingBehavior)
enum ResizingBehavior: Int {
case aspectFit
case aspectFill
case stretch
case center
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
var scales = CGSize.zero
scales.width = abs(target.width / rect.width)
scales.height = abs(target.height / rect.height)
switch self {
case .aspectFit:
scales.width = min(scales.width, scales.height)
scales.height = scales.width
case .aspectFill:
scales.width = max(scales.width, scales.height)
scales.height = scales.width
case .stretch:
break
case .center:
scales.width = 1
scales.height = 1
}
var result = rect.standardized
result.size.width *= scales.width
result.size.height *= scales.height
result.origin.x = target.minX + (target.width - result.width) / 2
result.origin.y = target.minY + (target.height - result.height) / 2
return result
}
}
private override init() {}
}
@@ -0,0 +1,51 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
extension UIViewController {
public var isPresentedAsStork: Bool {
return transitioningDelegate is SPStorkTransitioningDelegate
&& modalPresentationStyle == .custom
&& presentingViewController != nil
}
public func presentAsStork(_ controller: UIViewController, height: CGFloat? = nil) {
let transitionDelegate = SPStorkTransitioningDelegate()
transitionDelegate.customHeight = height
controller.transitioningDelegate = transitionDelegate
controller.modalPresentationStyle = .custom
controller.modalPresentationCapturesStatusBarAppearance = true
self.present(controller, animated: true, completion: nil)
}
public func presentAsStork(_ controller: UIViewController, height: CGFloat?, showIndicator: Bool, showCloseButton: Bool, complection: (() -> Void)? = nil) {
let transitionDelegate = SPStorkTransitioningDelegate()
transitionDelegate.customHeight = height
transitionDelegate.showCloseButton = showCloseButton
transitionDelegate.showIndicator = showIndicator
controller.transitioningDelegate = transitionDelegate
controller.modalPresentationStyle = .custom
controller.modalPresentationCapturesStatusBarAppearance = true
self.present(controller, animated: true, completion: complection)
}
}
@@ -21,6 +21,9 @@
import UIKit
func + (left: CGPoint, right: CGPoint) -> CGPoint {
return CGPoint(x: left.x + right.x, y: left.y + right.y)
public enum SPStorkHapticMoments {
case willPresent
case willDismiss
case willDismissIfRelease
}
@@ -21,13 +21,9 @@
import UIKit
public extension UIDevice {
@objc public protocol SPStorkControllerDelegate: class {
public var isIphone: Bool {
return UIDevice.current.userInterfaceIdiom == .phone
}
@objc optional func didDismissStorkBySwipe()
public var isIpad: Bool {
return UIDevice.current.userInterfaceIdiom == .pad
}
@objc optional func didDismissStorkByTap()
}
@@ -0,0 +1,84 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public enum SPStorkController {
static public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let controller = self.controller(for: scrollView) {
if let presentationController = controller.presentationController as? SPStorkPresentationController {
let translation = -(scrollView.contentOffset.y + scrollView.contentInset.top)
if translation >= 0 {
if controller.isBeingPresented { return }
scrollView.subviews.forEach {
$0.transform = CGAffineTransform(translationX: 0, y: -translation)
}
presentationController.setIndicator(style: scrollView.isTracking ? .line : .arrow)
if translation >= presentationController.translateForDismiss * 0.4 {
if !scrollView.isTracking && !scrollView.isDragging {
presentationController.presentedViewController.dismiss(animated: true, completion: {
presentationController.storkDelegate?.didDismissStorkBySwipe?()
})
return
}
}
if presentationController.pan?.state != UIGestureRecognizer.State.changed {
presentationController.scrollViewDidScroll(translation * 2)
}
} else {
presentationController.setIndicator(style: .arrow)
presentationController.scrollViewDidScroll(0)
}
if translation < -5 {
presentationController.setIndicator(visible: false, forse: (translation < -50))
} else {
presentationController.setIndicator(visible: true, forse: false)
}
}
}
}
static public var topScrollIndicatorInset: CGFloat {
return 6
}
static public func updatePresentingController(parent controller: UIViewController) {
if let presentationController = controller.presentedViewController?.presentationController as? SPStorkPresentationController {
presentationController.updatePresentingController()
}
}
static public func updatePresentingController(modal controller: UIViewController) {
if let presentationController = controller.presentationController as? SPStorkPresentationController {
presentationController.updatePresentingController()
}
}
static private func controller(for view: UIView) -> UIViewController? {
var nextResponder = view.next
while nextResponder != nil && !(nextResponder! is UIViewController) {
nextResponder = nextResponder!.next
}
return nextResponder as? UIViewController
}
}
@@ -0,0 +1,34 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public class SPStorkSegue: UIStoryboardSegue {
public var transitioningDelegate: SPStorkTransitioningDelegate?
override public func perform() {
transitioningDelegate = transitioningDelegate ?? SPStorkTransitioningDelegate()
destination.transitioningDelegate = transitioningDelegate
destination.modalPresentationStyle = .custom
super.perform()
}
}
@@ -28,9 +28,11 @@ final class SPStorkDismissingAnimationController: NSObject, UIViewControllerAnim
guard let presentedViewController = transitionContext.viewController(forKey: .from) else {
return
}
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
let containerView = transitionContext.containerView
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: finalFrameForPresentedView.width, height: finalFrameForPresentedView.height)
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
@@ -41,7 +43,7 @@ final class SPStorkDismissingAnimationController: NSObject, UIViewControllerAnim
animations: {
presentedViewController.view.frame = offscreenFrame
}) { finished in
transitionContext.completeTransition(finished)
transitionContext.completeTransition(finished)
}
}
@@ -0,0 +1,529 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
class SPStorkPresentationController: UIPresentationController, UIGestureRecognizerDelegate {
var swipeToDismissEnabled: Bool = true
var tapAroundToDismissEnabled: Bool = true
var showCloseButton: Bool = false
var showIndicator: Bool = true
var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
var hideIndicatorWhenScroll: Bool = false
var customHeight: CGFloat? = nil
var translateForDismiss: CGFloat = 200
var hapticMoments: [SPStorkHapticMoments] = [.willDismissIfRelease]
var transitioningDelegate: SPStorkTransitioningDelegate?
weak var storkDelegate: SPStorkControllerDelegate?
var pan: UIPanGestureRecognizer?
var tap: UITapGestureRecognizer?
private var closeButton = SPStorkCloseButton()
private var indicatorView = SPStorkIndicatorView()
private var gradeView: UIView = UIView()
private let snapshotViewContainer = UIView()
private var snapshotView: UIView?
private let backgroundView = UIView()
private var snapshotViewTopConstraint: NSLayoutConstraint?
private var snapshotViewWidthConstraint: NSLayoutConstraint?
private var snapshotViewAspectRatioConstraint: NSLayoutConstraint?
private var workGester: Bool = false
private var startDismissing: Bool = false
private var afterReleaseDismissing: Bool = false
private var topSpace: CGFloat {
let statusBarHeight: CGFloat = UIApplication.shared.statusBarFrame.height
return (statusBarHeight < 25) ? 30 : statusBarHeight
}
private let alpha: CGFloat = 0.51
var cornerRadius: CGFloat = 10
private var scaleForPresentingView: CGFloat {
guard let containerView = containerView else { return 0 }
let factor = 1 - ((self.cornerRadius + 3) * 2 / containerView.frame.width)
return factor
}
private var feedbackGenerator: UIImpactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light)
override var presentedView: UIView? {
let view = self.presentedViewController.view
if view?.frame.origin == CGPoint.zero {
view?.frame = self.frameOfPresentedViewInContainerView
}
return view
}
override var frameOfPresentedViewInContainerView: CGRect {
guard let containerView = containerView else { return .zero }
let baseY: CGFloat = self.topSpace + 13
let maxHeight: CGFloat = containerView.bounds.height - baseY
var height: CGFloat = maxHeight
if let customHeight = self.customHeight {
if customHeight < maxHeight {
height = customHeight
} else {
print("SPStorkController - Custom height change to default value. Your height more maximum value")
}
}
return CGRect(x: 0, y: containerView.bounds.height - height, width: containerView.bounds.width, height: height)
}
override func presentationTransitionWillBegin() {
super.presentationTransitionWillBegin()
if !self.hapticMoments.isEmpty {
self.feedbackGenerator.prepare()
}
guard let containerView = self.containerView, let presentedView = self.presentedView, let window = containerView.window else { return }
if self.showIndicator {
self.indicatorView.color = self.indicatorColor
let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.dismissAction))
tap.cancelsTouchesInView = false
self.indicatorView.addGestureRecognizer(tap)
presentedView.addSubview(self.indicatorView)
self.indicatorView.translatesAutoresizingMaskIntoConstraints = false
self.indicatorView.widthAnchor.constraint(equalToConstant: 36).isActive = true
self.indicatorView.heightAnchor.constraint(equalToConstant: 13).isActive = true
self.indicatorView.centerXAnchor.constraint(equalTo: presentedView.centerXAnchor).isActive = true
self.indicatorView.topAnchor.constraint(equalTo: presentedView.topAnchor, constant: 12).isActive = true
}
self.updateLayoutIndicator()
self.indicatorView.style = .arrow
self.gradeView.alpha = 0
if self.showCloseButton {
self.closeButton.addTarget(self, action: #selector(self.dismissAction), for: .touchUpInside)
presentedView.addSubview(self.closeButton)
}
self.updateLayoutCloseButton()
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
containerView.insertSubview(self.snapshotViewContainer, belowSubview: presentedViewController.view)
self.snapshotViewContainer.frame = initialFrame
self.updateSnapshot()
self.snapshotView?.layer.cornerRadius = 0
self.backgroundView.backgroundColor = UIColor.black
self.backgroundView.translatesAutoresizingMaskIntoConstraints = false
containerView.insertSubview(self.backgroundView, belowSubview: self.snapshotViewContainer)
NSLayoutConstraint.activate([
self.backgroundView.topAnchor.constraint(equalTo: window.topAnchor),
self.backgroundView.leftAnchor.constraint(equalTo: window.leftAnchor),
self.backgroundView.rightAnchor.constraint(equalTo: window.rightAnchor),
self.backgroundView.bottomAnchor.constraint(equalTo: window.bottomAnchor)
])
let transformForSnapshotView = CGAffineTransform.identity
.translatedBy(x: 0, y: -snapshotViewContainer.frame.origin.y)
.translatedBy(x: 0, y: self.topSpace)
.translatedBy(x: 0, y: -snapshotViewContainer.frame.height / 2)
.scaledBy(x: scaleForPresentingView, y: scaleForPresentingView)
.translatedBy(x: 0, y: snapshotViewContainer.frame.height / 2)
self.addCornerRadiusAnimation(for: self.snapshotView, cornerRadius: self.cornerRadius, duration: 0.6)
self.snapshotView?.layer.masksToBounds = true
if #available(iOS 11.0, *) {
presentedView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
}
presentedView.layer.cornerRadius = self.cornerRadius
presentedView.layer.masksToBounds = true
var rootSnapshotView: UIView?
var rootSnapshotRoundedView: UIView?
if presentingViewController.isPresentedAsStork {
guard let rootController = presentingViewController.presentingViewController, let snapshotView = rootController.view.snapshotView(afterScreenUpdates: false) else { return }
containerView.insertSubview(snapshotView, aboveSubview: self.backgroundView)
snapshotView.frame = initialFrame
snapshotView.transform = transformForSnapshotView
snapshotView.alpha = 1 - self.alpha
snapshotView.layer.cornerRadius = self.cornerRadius
snapshotView.contentMode = .top
snapshotView.layer.masksToBounds = true
rootSnapshotView = snapshotView
let snapshotRoundedView = UIView()
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
snapshotRoundedView.layer.masksToBounds = true
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
snapshotRoundedView.frame = initialFrame
snapshotRoundedView.transform = transformForSnapshotView
rootSnapshotRoundedView = snapshotRoundedView
}
presentedViewController.transitionCoordinator?.animate(
alongsideTransition: { [weak self] context in
guard let `self` = self else { return }
self.snapshotView?.transform = transformForSnapshotView
self.gradeView.alpha = self.alpha
}, completion: { _ in
self.snapshotView?.transform = .identity
rootSnapshotView?.removeFromSuperview()
rootSnapshotRoundedView?.removeFromSuperview()
})
if self.hapticMoments.contains(.willPresent) {
self.feedbackGenerator.impactOccurred()
}
}
override func presentationTransitionDidEnd(_ completed: Bool) {
super.presentationTransitionDidEnd(completed)
guard let containerView = containerView else { return }
self.updateSnapshot()
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
self.snapshotViewContainer.transform = .identity
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = false
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
self.updateSnapshotAspectRatio()
if self.tapAroundToDismissEnabled {
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.dismissAction))
self.tap?.cancelsTouchesInView = false
self.snapshotViewContainer.addGestureRecognizer(self.tap!)
}
if self.swipeToDismissEnabled {
self.pan = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan))
self.pan!.delegate = self
self.pan!.maximumNumberOfTouches = 1
self.pan!.cancelsTouchesInView = false
self.presentedViewController.view.addGestureRecognizer(self.pan!)
}
}
@objc func dismissAction() {
self.presentingViewController.view.endEditing(true)
self.presentedViewController.view.endEditing(true)
self.presentedViewController.dismiss(animated: true, completion: {
self.storkDelegate?.didDismissStorkByTap?()
})
}
override func dismissalTransitionWillBegin() {
super.dismissalTransitionWillBegin()
guard let containerView = containerView else { return }
self.startDismissing = true
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
let initialTransform = CGAffineTransform.identity
.translatedBy(x: 0, y: -initialFrame.origin.y)
.translatedBy(x: 0, y: self.topSpace)
.translatedBy(x: 0, y: -initialFrame.height / 2)
.scaledBy(x: scaleForPresentingView, y: scaleForPresentingView)
.translatedBy(x: 0, y: initialFrame.height / 2)
self.snapshotViewTopConstraint?.isActive = false
self.snapshotViewWidthConstraint?.isActive = false
self.snapshotViewAspectRatioConstraint?.isActive = false
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = true
self.snapshotViewContainer.frame = initialFrame
self.snapshotViewContainer.transform = initialTransform
let finalCornerRadius = presentingViewController.isPresentedAsStork ? self.cornerRadius : 0
let finalTransform: CGAffineTransform = .identity
self.addCornerRadiusAnimation(for: self.snapshotView, cornerRadius: finalCornerRadius, duration: 0.6)
var rootSnapshotView: UIView?
var rootSnapshotRoundedView: UIView?
if presentingViewController.isPresentedAsStork {
guard let rootController = presentingViewController.presentingViewController, let snapshotView = rootController.view.snapshotView(afterScreenUpdates: false) else { return }
containerView.insertSubview(snapshotView, aboveSubview: backgroundView)
snapshotView.frame = initialFrame
snapshotView.transform = initialTransform
snapshotView.contentMode = .top
rootSnapshotView = snapshotView
snapshotView.layer.cornerRadius = self.cornerRadius
snapshotView.layer.masksToBounds = true
let snapshotRoundedView = UIView()
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
snapshotRoundedView.layer.masksToBounds = true
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(self.alpha)
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
snapshotRoundedView.frame = initialFrame
snapshotRoundedView.transform = initialTransform
rootSnapshotRoundedView = snapshotRoundedView
}
presentedViewController.transitionCoordinator?.animate(
alongsideTransition: { [weak self] context in
guard let `self` = self else { return }
self.snapshotView?.transform = .identity
self.snapshotViewContainer.transform = finalTransform
self.gradeView.alpha = 0
if self.hapticMoments.contains(.willDismiss) {
self.feedbackGenerator.impactOccurred()
}
}, completion: { _ in
rootSnapshotView?.removeFromSuperview()
rootSnapshotRoundedView?.removeFromSuperview()
})
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
super.dismissalTransitionDidEnd(completed)
guard let containerView = containerView else { return }
self.backgroundView.removeFromSuperview()
self.snapshotView?.removeFromSuperview()
self.snapshotViewContainer.removeFromSuperview()
self.indicatorView.removeFromSuperview()
self.closeButton.removeFromSuperview()
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
presentedViewController.view.frame = offscreenFrame
presentedViewController.view.transform = .identity
}
}
extension SPStorkPresentationController {
@objc func handlePan(gestureRecognizer: UIPanGestureRecognizer) {
guard gestureRecognizer.isEqual(self.pan), self.swipeToDismissEnabled else { return }
switch gestureRecognizer.state {
case .began:
self.workGester = true
self.indicatorView.style = .line
self.presentingViewController.view.layer.removeAllAnimations()
self.presentingViewController.view.endEditing(true)
self.presentedViewController.view.endEditing(true)
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: containerView)
case .changed:
self.workGester = true
let translation = gestureRecognizer.translation(in: presentedView)
if self.swipeToDismissEnabled {
self.updatePresentedViewForTranslation(inVerticalDirection: translation.y)
} else {
gestureRecognizer.setTranslation(.zero, in: presentedView)
}
case .ended:
self.workGester = false
let translation = gestureRecognizer.translation(in: presentedView).y
if translation >= self.translateForDismiss {
self.presentedViewController.dismiss(animated: true, completion: {
self.storkDelegate?.didDismissStorkBySwipe?()
})
} else {
self.indicatorView.style = .arrow
UIView.animate(
withDuration: 0.6,
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: [.curveEaseOut, .allowUserInteraction],
animations: {
self.snapshotView?.transform = .identity
self.presentedView?.transform = .identity
self.gradeView.alpha = self.alpha
})
}
default:
break
}
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if let gester = gestureRecognizer as? UIPanGestureRecognizer {
let velocity = gester.velocity(in: self.presentedViewController.view)
return abs(velocity.y) > abs(velocity.x)
}
return true
}
func scrollViewDidScroll(_ translation: CGFloat) {
if !self.workGester {
self.updatePresentedViewForTranslation(inVerticalDirection: translation)
}
}
func updatePresentingController() {
if self.startDismissing { return }
self.updateSnapshot()
}
func setIndicator(style: SPStorkIndicatorView.Style) {
self.indicatorView.style = style
}
func setIndicator(visible: Bool, forse: Bool) {
guard self.hideIndicatorWhenScroll else { return }
let newAlpha: CGFloat = visible ? 1 : 0
if forse {
self.indicatorView.layer.removeAllAnimations()
self.indicatorView.alpha = newAlpha
return
}
if self.indicatorView.alpha == newAlpha {
return
}
UIView.animate(withDuration: 0.18, animations: {
self.indicatorView.alpha = newAlpha
})
}
private func updatePresentedViewForTranslation(inVerticalDirection translation: CGFloat) {
if self.startDismissing { return }
let elasticThreshold: CGFloat = 120
let translationFactor: CGFloat = 1 / 2
if translation >= 0 {
let translationForModal: CGFloat = {
if translation >= elasticThreshold {
let frictionLength = translation - elasticThreshold
let frictionTranslation = 30 * atan(frictionLength / 120) + frictionLength / 10
return frictionTranslation + (elasticThreshold * translationFactor)
} else {
return translation * translationFactor
}
}()
self.presentedView?.transform = CGAffineTransform(translationX: 0, y: translationForModal)
let scaleFactor = 1 + (translationForModal / 5000)
self.snapshotView?.transform = CGAffineTransform.init(scaleX: scaleFactor, y: scaleFactor)
let gradeFactor = 1 + (translationForModal / 7000)
self.gradeView.alpha = self.alpha - ((gradeFactor - 1) * 15)
} else {
self.presentedView?.transform = CGAffineTransform.identity
}
if self.swipeToDismissEnabled {
let afterRealseDismissing = (translation >= self.translateForDismiss)
if afterRealseDismissing != self.afterReleaseDismissing {
self.afterReleaseDismissing = afterRealseDismissing
if self.hapticMoments.contains(.willDismissIfRelease) {
self.feedbackGenerator.impactOccurred()
}
}
}
}
}
extension SPStorkPresentationController {
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
guard let containerView = containerView else { return }
self.updateSnapshotAspectRatio()
if presentedViewController.view.isDescendant(of: containerView) {
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { contex in
self.updateLayoutIndicator()
self.updateLayoutCloseButton()
}, completion: { [weak self] _ in
self?.updateSnapshotAspectRatio()
self?.updateSnapshot()
})
}
private func updateLayoutIndicator() {
self.indicatorView.style = .line
self.indicatorView.sizeToFit()
//self.indicatorView.frame.origin.y = 12
//self.indicatorView.center.x = presentedView.frame.width / 2
}
private func updateLayoutCloseButton() {
guard let presentedView = self.presentedView else { return }
self.closeButton.sizeToFit()
self.closeButton.layout(bottomX: presentedView.frame.width - 19, y: 19)
}
private func updateSnapshot() {
guard let currentSnapshotView = presentingViewController.view.snapshotView(afterScreenUpdates: true) else { return }
self.snapshotView?.removeFromSuperview()
self.snapshotViewContainer.addSubview(currentSnapshotView)
self.constraints(view: currentSnapshotView, to: self.snapshotViewContainer)
self.snapshotView = currentSnapshotView
self.snapshotView?.layer.cornerRadius = self.cornerRadius
self.snapshotView?.layer.masksToBounds = true
if #available(iOS 11.0, *) {
snapshotView?.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
}
self.gradeView.removeFromSuperview()
self.gradeView.backgroundColor = UIColor.black
self.snapshotView!.addSubview(self.gradeView)
self.constraints(view: self.gradeView, to: self.snapshotView!)
}
private func updateSnapshotAspectRatio() {
guard let containerView = containerView, snapshotViewContainer.translatesAutoresizingMaskIntoConstraints == false else { return }
self.snapshotViewTopConstraint?.isActive = false
self.snapshotViewWidthConstraint?.isActive = false
self.snapshotViewAspectRatioConstraint?.isActive = false
let snapshotReferenceSize = presentingViewController.view.frame.size
let aspectRatio = snapshotReferenceSize.width / snapshotReferenceSize.height
self.snapshotViewTopConstraint = snapshotViewContainer.topAnchor.constraint(equalTo: containerView.topAnchor, constant: self.topSpace)
self.snapshotViewWidthConstraint = snapshotViewContainer.widthAnchor.constraint(equalTo: containerView.widthAnchor, multiplier: scaleForPresentingView)
self.snapshotViewAspectRatioConstraint = snapshotViewContainer.widthAnchor.constraint(equalTo: snapshotViewContainer.heightAnchor, multiplier: aspectRatio)
self.snapshotViewTopConstraint?.isActive = true
self.snapshotViewWidthConstraint?.isActive = true
self.snapshotViewAspectRatioConstraint?.isActive = true
}
private func constraints(view: UIView, to superView: UIView) {
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: superView.topAnchor),
view.leftAnchor.constraint(equalTo: superView.leftAnchor),
view.rightAnchor.constraint(equalTo: superView.rightAnchor),
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor)
])
}
private func addCornerRadiusAnimation(for view: UIView?, cornerRadius: CGFloat, duration: CFTimeInterval) {
guard let view = view else { return }
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
animation.fromValue = view.layer.cornerRadius
animation.toValue = cornerRadius
animation.duration = duration
view.layer.add(animation, forKey: "cornerRadius")
view.layer.cornerRadius = cornerRadius
}
}
@@ -24,14 +24,15 @@ import UIKit
final class SPStorkPresentingAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let presentedViewController = transitionContext.viewController(forKey: .to) else {
return
}
guard let presentedViewController = transitionContext.viewController(forKey: .to) else { return }
let containerView = transitionContext.containerView
containerView.addSubview(presentedViewController.view)
presentedViewController.view.frame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
presentedViewController.view.frame = finalFrameForPresentedView
presentedViewController.view.frame.origin.y = containerView.bounds.height
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
@@ -23,9 +23,32 @@ import UIKit
public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
public var swipeToDismissEnabled: Bool = true
public var tapAroundToDismissEnabled: Bool = true
public var showCloseButton: Bool = false
public var showIndicator: Bool = true
public var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
public var hideIndicatorWhenScroll: Bool = false
public var customHeight: CGFloat? = nil
public var translateForDismiss: CGFloat = 200
public var cornerRadius: CGFloat = 10
public var hapticMoments: [SPStorkHapticMoments] = [.willDismissIfRelease]
public weak var storkDelegate: SPStorkControllerDelegate? = nil
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let controller = SPStorkPresentationController(presentedViewController: presented, presenting: presenting)
controller.swipeToDismissEnabled = self.swipeToDismissEnabled
controller.tapAroundToDismissEnabled = self.tapAroundToDismissEnabled
controller.showCloseButton = self.showCloseButton
controller.showIndicator = self.showIndicator
controller.indicatorColor = self.indicatorColor
controller.hideIndicatorWhenScroll = self.hideIndicatorWhenScroll
controller.customHeight = self.customHeight
controller.translateForDismiss = self.translateForDismiss
controller.cornerRadius = self.cornerRadius
controller.hapticMoments = self.hapticMoments
controller.transitioningDelegate = self
controller.storkDelegate = self.storkDelegate
return controller
}
@@ -0,0 +1,78 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
open class SPStorkCloseButton: UIButton {
let iconView = SPStorkCloseView()
var widthIconFactor: CGFloat = 1
var heightIconFactor: CGFloat = 1
var color = UIColor.blue {
didSet {
self.iconView.color = self.color
}
}
override open var isHighlighted: Bool {
didSet {
self.iconView.color = self.color.withAlphaComponent(self.isHighlighted ? 0.7 : 1)
}
}
init() {
super.init(frame: .zero)
self.commonInit()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func commonInit() {
self.iconView.isUserInteractionEnabled = false
self.addSubview(self.iconView)
self.backgroundColor = UIColor.init(red: 239 / 255.0, green: 239 / 255.0, blue: 244 / 255.0, alpha: 1)
self.color = UIColor.init(red: 142 / 255.0, green: 142 / 255.0, blue: 147 / 255.0, alpha: 1)
self.widthIconFactor = 0.36
self.heightIconFactor = 0.36
}
func layout(bottomX: CGFloat, y: CGFloat) {
self.sizeToFit()
self.frame.origin.x = bottomX - self.frame.width
self.frame.origin.y = y
}
override open func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = self.frame.width / 2
self.iconView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width * self.widthIconFactor, height: self.frame.height * self.heightIconFactor)
self.iconView.center = CGPoint.init(x: self.frame.width / 2, y: self.frame.height / 2)
}
override open func sizeToFit() {
super.sizeToFit()
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 30, height: 30)
}
}
@@ -21,33 +21,30 @@
import UIKit
public class SPCollectionViewCell: UICollectionViewCell {
open class SPStorkCloseView: UIView {
var currentIndexPath: IndexPath?
public override func prepareForReuse() {
super.prepareForReuse()
self.currentIndexPath = nil
var color = UIColor.blue {
didSet {
self.setNeedsDisplay()
}
}
}
public class SPCollectionContainerCell<ContentView: UIView>: UICollectionViewCell {
let view = ContentView.init()
var currentIndexPath: IndexPath?
init() {
super.init(frame: CGRect.zero)
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
private func commonInit() {
self.backgroundColor = UIColor.clear
self.addSubview(view)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func layoutSubviews() {
super.layoutSubviews()
self.view.setEqualsFrameFromBounds(self)
override open func draw(_ rect: CGRect) {
super.draw(rect)
SPStorkCodeDraw.drawClose(frame: rect, resizing: .aspectFit, color: self.color)
}
}
@@ -21,58 +21,69 @@
import UIKit
class SPStorkIndicatorView: UIView {
open class SPStorkIndicatorView: UIView {
var style: Style = .line {
didSet {
switch self.style {
case .line:
SPAnimationSpring.animate(0.5, animations: {
self.animate {
self.leftView.transform = .identity
self.rightView.transform = .identity
}, options: .curveEaseOut)
}
case .arrow:
SPAnimationSpring.animate(0.5, animations: {
self.animate {
let angle = CGFloat(20 * Float.pi / 180)
self.leftView.transform = CGAffineTransform.init(rotationAngle: angle)
self.rightView.transform = CGAffineTransform.init(rotationAngle: -angle)
}, options: .curveEaseOut)
}
}
}
}
var color: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1) {
didSet {
self.leftView.backgroundColor = self.color
self.rightView.backgroundColor = self.color
}
}
private var leftView: UIView = UIView()
private var rightView: UIView = UIView()
init() {
super.init(frame: .zero)
self.backgroundColor = UIColor.clear
self.addSubview(self.leftView)
self.addSubview(self.rightView)
self.leftView.backgroundColor = UIColor.init(hex: "CAC9CF")
self.rightView.backgroundColor = UIColor.init(hex: "CAC9CF")
self.color = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
}
required init?(coder aDecoder: NSCoder) {
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func sizeToFit() {
override open func sizeToFit() {
super.sizeToFit()
self.setWidth(36)
self.setHeight(13)
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 36, height: 13)
let height: CGFloat = 5
let correction = height / 2
self.leftView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width / 2 + correction, height: height)
self.leftView.center.y = self.frame.height / 2
self.leftView.round()
self.leftView.layer.cornerRadius = min(self.leftView.frame.width, self.leftView.frame.height) / 2
self.rightView.frame = CGRect.init(x: self.frame.width / 2 - correction, y: 0, width: self.frame.width / 2 + correction, height: height)
self.rightView.center.y = self.frame.height / 2
self.rightView.round()
self.rightView.layer.cornerRadius = min(self.leftView.frame.width, self.leftView.frame.height) / 2
}
private func animate(animations: @escaping (() -> Void)) {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.beginFromCurrentState, .curveEaseOut], animations: {
animations()
})
}
enum Style {
@@ -21,7 +21,7 @@
import UIKit
public class SPAnimation {
enum SPAnimation {
static func animate(_ duration: TimeInterval,
animations: (() -> Void)!,
@@ -47,7 +47,7 @@ public class SPAnimation {
withComplection completion: (() -> Void)! = {}) {
var optionsWithRepeatition = options
optionsWithRepeatition.insert([.autoreverse, .repeat])
optionsWithRepeatition.insert([.autoreverse, .repeat, .allowUserInteraction])
self.animate(
duration,
@@ -21,11 +21,11 @@
import UIKit
public class SPAnimationAlpha {
class SPAnimationAlpha {
fileprivate static let durationListAnimation: TimeInterval = 0.45
fileprivate static let coefLenthForTransition: CGFloat = 2.8
fileprivate static let delayPerItem: TimeInterval = 0.09
static let durationListAnimation: TimeInterval = 0.45
static let coefLenthForTransition: CGFloat = 2.8
static let delayPerItem: TimeInterval = 0.09
static func hideList(_ duration: TimeInterval = durationListAnimation,
views: [UIView],
@@ -21,10 +21,10 @@
import UIKit
public class SPAnimationSpring {
class SPAnimationSpring {
fileprivate static let spring: CGFloat = 1
fileprivate static let velocity: CGFloat = 1
static let spring: CGFloat = 1
static let velocity: CGFloat = 1
static func animate(_ duration: TimeInterval,
animations: (() -> Void)!,
@@ -21,11 +21,11 @@
import UIKit
public class SPAnimationUpward {
class SPAnimationUpward {
fileprivate static let durationListAnimation: TimeInterval = 0.45
fileprivate static let coefLenthForTransition: CGFloat = 2.8
fileprivate static let delayPerItem: TimeInterval = 0.09
static let durationListAnimation: TimeInterval = 0.45
static let coefLenthForTransition: CGFloat = 2.8
static let delayPerItem: TimeInterval = 0.09
static func hide(_ duration: TimeInterval,
view: UIView,
@@ -0,0 +1,73 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
enum SPApp {
static var udid: String? {
return UIDevice.current.identifierForVendor?.uuidString
}
static var displayName: String? {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
}
static var rootController: UIViewController? {
return UIApplication.shared.keyWindow?.rootViewController
}
static var safeArea: UIEdgeInsets {
if #available(iOS 11.0, *) {
return UIApplication.shared.keyWindow?.safeArea ?? UIEdgeInsets.zero
} else {
return UIEdgeInsets.zero
}
}
static func set(rootController: UIViewController, animatable: Bool = true) {
rootController.view.frame = UIScreen.main.bounds
let replaceRootViewController = {
UIApplication.shared.keyWindow?.rootViewController = rootController
}
if animatable {
UIView.transition(
with: UIApplication.shared.keyWindow ?? UIWindow(),
duration: 0.5,
options: UIView.AnimationOptions.transitionCrossDissolve,
animations: {
replaceRootViewController()
}, completion: nil)
} else {
replaceRootViewController()
}
}
static func set(elementsColor: UIColor) {
UINavigationController.elementsColor = elementsColor
UIAlertController.elementsColor = elementsColor
UITabBarController.elementsColor = elementsColor
UITabBar.appearance().tintColor = elementsColor
}
}
@@ -21,25 +21,24 @@
import UIKit
struct SPLaunch {
extension SPApp {
static func run() {
self.count += 1
}
static var count: Int {
get {
return UserDefaults.standard.value(forKey: "SPLaunchCount") as? Int ?? 0
struct Badge {
static var number: Int {
get {
return UIApplication.shared.applicationIconBadgeNumber
}
set {
UIApplication.shared.applicationIconBadgeNumber = newValue
}
}
set {
UserDefaults.standard.set(newValue, forKey: "SPLaunchCount")
static func reset() {
UIApplication.shared.applicationIconBadgeNumber = 0
}
private init() {}
}
static var isFirstOpen: Bool {
return (self.count == 1) || (self.count == 0)
}
private init() {}
}
@@ -21,25 +21,28 @@
import UIKit
class SPScrollViewController: SPBaseViewController {
extension SPApp {
let scrollView = SPScrollView()
override func viewDidLoad() {
enum Launch {
self.view.backgroundColor = SPNativeStyleKit.Colors.customGray
static func run() {
self.count += 1
if (UserDefaults.standard.object(forKey: "SPDateFirstLaunch") as? Date) == nil {
UserDefaults.standard.set(Date(), forKey: "SPDateFirstLaunch")
}
}
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.showsVerticalScrollIndicator = true
self.scrollView.indicatorStyle = .black
self.view.addSubview(self.scrollView)
static var count: Int {
get { return UserDefaults.standard.value(forKey: "SPLaunchCount") as? Int ?? 0 }
set { UserDefaults.standard.set(newValue, forKey: "SPLaunchCount") }
}
super.viewDidLoad()
}
override func updateLayout(with size: CGSize) {
self.scrollView.frame = CGRect.init(origin: .zero, size: size)
self.scrollView.contentSize = CGSize.init(width: size.width, height: size.height * 2)
super.updateLayout(with: size)
static var isFirstLaunch: Bool {
return (self.count == 1) || (self.count == 0)
}
static var dateFirstLaunch: Date {
return ((UserDefaults.standard.object(forKey: "SPDateFirstLaunch") as? Date) ?? Date())
}
}
}
@@ -0,0 +1,88 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
import SafariServices
extension SPApp {
static func open(app: SPSystemApp) {
switch app {
case SPSystemApp.photos:
guard let settingsUrl = URL(string: "photos-redirect://") else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
print("SPApp - Photos opened: \(success)")
})
} else {
UIApplication.shared.openURL(settingsUrl as URL)
}
} else {
print("SPApp - Photos not opened")
}
case SPSystemApp.setting:
DispatchQueue.main.async {
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else {
return
}
if UIApplication.shared.canOpenURL(settingsUrl) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
print("SPApp - Settings opened: \(success)")
})
} else {
UIApplication.shared.openURL(settingsUrl as URL)
}
} else {
print("SPApp - Settings not opened")
}
}
}
}
static func open(link: String) {
guard let url = URL(string: link) else {
print("SPOpener - can not create URL")
return
}
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
}
static func open(link: String, on controller: UIViewController) {
guard let url = URL(string: link) else {
print("SPOpener - can not create URL")
return
}
let safariController = SFSafariViewController.init(url: url)
controller.present(safariController, animated: true, completion: nil)
}
}
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}
@@ -25,11 +25,11 @@ import StoreKit
struct SPAppStore {
static func link(appID: String) -> String {
return "https://itunes.apple.com/by/app/id" + appID
return "https://itunes.apple.com/app/id" + appID
}
static func open(appID: String) {
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(appID)"),
static func open(app id: String) {
if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(id)"),
UIApplication.shared.canOpenURL(url) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
@@ -56,6 +56,12 @@ struct SPAppStore {
}
}
static func requestReview() {
if #available(iOS 10.3, *) {
SKStoreReviewController.requestReview()
}
}
static func isUpdateAvailable(completion: @escaping (Bool)->()) {
guard let info = Bundle.main.infoDictionary,
@@ -128,7 +134,6 @@ extension String {
}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}
@@ -22,21 +22,20 @@
import UIKit
import AVFoundation
public struct SPAudio {
struct SPAudio {
static func notStopBackgroundMusic() {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category(rawValue: convertFromAVAudioSessionCategory(AVAudioSession.Category.ambient)), mode: AVAudioSession.Mode.default)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("SPAudio - notStopBackgroundMusic, error")
}
}
private init() {}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String {
return input.rawValue
}
@@ -22,12 +22,12 @@
import UIKit
import AVFoundation
public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
fileprivate var player: AVAudioPlayer = AVAudioPlayer()
fileprivate var endPlayingComplection: (()->())? = nil
func play(fileName: String, complection: (()->())? = nil) {
func play(fileName: String, complection: (()->())? = nil, volume: Float = 1) {
self.endPlayingComplection?()
self.player = AVAudioPlayer()
let url = Bundle.main.url(forResource: fileName, withExtension: nil)
@@ -37,7 +37,7 @@ public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
}
do {
self.player = try AVAudioPlayer(contentsOf: url!)
player.volume = 1
player.volume = volume
player.delegate = self
player.prepareToPlay()
player.play()
@@ -51,7 +51,7 @@ public class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
player.stop()
}
public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
self.endPlayingComplection?()
}
}
@@ -23,9 +23,9 @@ import UIKit
extension SPCodeDraw {
public class AudioIconPack : NSObject {
class AudioIconPack : NSObject {
@objc dynamic public class func drawPlay(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawPlay(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -53,7 +53,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawPause(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawPause(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -90,7 +90,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawStop(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawStop(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -118,13 +118,13 @@ extension SPCodeDraw {
}
@objc(StyleKitNameResizingBehavior)
public enum ResizingBehavior: Int {
enum ResizingBehavior: Int {
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
case stretch /// The content is stretched to match the entire target rectangle.
case center /// The content is centered in the target rectangle, but it is NOT resized.
public func apply(rect: CGRect, target: CGRect) -> CGRect {
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
@@ -21,4 +21,4 @@
import UIKit
struct SPCodeDraw { private init(){} }
struct SPCodeDraw { private init() {} }
@@ -23,9 +23,9 @@ import UIKit
extension SPCodeDraw {
public class SocialIconPack : NSObject {
class SocialIconPack : NSObject {
@objc dynamic public class func drawInstagram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawInstagram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -115,7 +115,7 @@ extension SPCodeDraw {
}
@objc dynamic public class func drawVK(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawVK(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -167,7 +167,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawWhatsapp(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawWhatsapp(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -237,7 +237,7 @@ extension SPCodeDraw {
}
@objc dynamic public class func drawTelegram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawTelegram(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 40, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -313,7 +313,7 @@ extension SPCodeDraw {
}
@objc dynamic public class func drawFacebook(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawFacebook(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -352,7 +352,7 @@ extension SPCodeDraw {
}
@objc dynamic public class func drawViber(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawViber(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 41, height: 40), resizing: ResizingBehavior = .aspectFit, fillColor: UIColor = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000)) {
//// General Declarations
let context = UIGraphicsGetCurrentContext()!
@@ -443,13 +443,13 @@ extension SPCodeDraw {
}
@objc(SocialIconStyleKitResizingBehavior)
public enum ResizingBehavior: Int {
enum ResizingBehavior: Int {
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
case stretch /// The content is stretched to match the entire target rectangle.
case center /// The content is centered in the target rectangle, but it is NOT resized.
public func apply(rect: CGRect, target: CGRect) -> CGRect {
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
@@ -23,15 +23,15 @@ import UIKit
extension SPCodeDraw {
public class SystemIconPack : NSObject {
class SystemIconPack : NSObject {
private struct Cache {
static let gradient: CGGradient = CGGradient(colorsSpace: nil, colors: [UIColor.red.cgColor, UIColor.red.cgColor] as CFArray, locations: [0, 1])!
}
@objc dynamic public class var gradient: CGGradient { return Cache.gradient }
@objc dynamic class var gradient: CGGradient { return Cache.gradient }
@objc dynamic public class func drawFavorite(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawFavorite(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -80,7 +80,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawFavoriteFill(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
@objc dynamic class func drawFavoriteFill(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.478, blue: 1.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -113,7 +113,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawShare(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawShare(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -154,7 +154,7 @@ extension SPCodeDraw {
context.restoreGState()
}
@objc dynamic public class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
@objc dynamic class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
@@ -192,13 +192,13 @@ extension SPCodeDraw {
}
@objc(StyleKitNameResizingBehavior)
public enum ResizingBehavior: Int {
enum ResizingBehavior: Int {
case aspectFit /// The content is proportionally resized to fit into the target rectangle.
case aspectFill /// The content is proportionally resized to completely fill the target rectangle.
case stretch /// The content is stretched to match the entire target rectangle.
case center /// The content is centered in the target rectangle, but it is NOT resized.
public func apply(rect: CGRect, target: CGRect) -> CGRect {
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
@@ -0,0 +1,42 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
struct SPConstraints {
static func setEqualSizeSuperview(for view: UIView) {
if let superView = view.superview {
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: superView.topAnchor),
view.leftAnchor.constraint(equalTo: superView.leftAnchor),
view.rightAnchor.constraint(equalTo: superView.rightAnchor),
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor)
])
}
}
private init() {}
}
@@ -21,7 +21,7 @@
import Foundation
public func delay(_ delay:Double, closure:@escaping ()->()) {
func delay(_ delay:Double, closure: @escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when) {
closure()
@@ -23,12 +23,12 @@ import UIKit
struct SPDevice {
static var isIphone: Bool {
return UIDevice.current.isIphone
static var iphone: Bool {
return UIDevice.current.userInterfaceIdiom == .phone
}
static var isIpad: Bool {
return UIDevice.current.isIpad
static var ipad: Bool {
return UIDevice.current.userInterfaceIdiom == .pad
}
struct Orientation {
@@ -30,6 +30,7 @@ struct SPDownloader {
}
return
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
@@ -47,6 +48,8 @@ struct SPDownloader {
}
}.resume()
}
private init() {}
}
@@ -23,10 +23,8 @@ import Foundation
extension Array {
func takeElements(count: Int) -> Array {
if (count < self.count) {
return Array(self[0..<count])
}
func get(count: Int) -> Array {
if (count < self.count) { return Array(self[0..<count]) }
return Array(self)
}
}
@@ -35,11 +33,8 @@ extension Array where Element: Equatable {
mutating func removeDuplicates() {
var result = [Element]()
for value in self {
if result.contains(value) == false {
result.append(value)
}
if result.contains(value) == false { result.append(value) }
}
self = result
}
@@ -48,9 +43,7 @@ extension Array where Element: Equatable {
extension Array where Element: Hashable {
func after(item: Element) -> Element? {
if let index = self.index(of: item), index + 1 < self.count {
return self[index + 1]
}
if let index = self.firstIndex(of: item), index + 1 < self.count { return self[index + 1] }
return nil
}
}
@@ -0,0 +1,51 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
extension CGRect {
var bottomX: CGFloat {
get { return self.origin.x + self.width }
set { self.origin.x = newValue - self.width }
}
var bottomY: CGFloat {
get { return self.origin.y + self.height }
set { self.origin.y = newValue - self.height }
}
var minSide: CGFloat {
return min(self.width, self.height)
}
mutating func set(width: CGFloat) {
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: self.height)
}
mutating func set(height: CGFloat) {
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: self.width, height: height)
}
mutating func set(width: CGFloat, height: CGFloat) {
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: height)
}
}
@@ -23,15 +23,15 @@ import UIKit
extension CGSize {
func resize(newWidth: CGFloat) -> CGSize {
func resize(width: CGFloat) -> CGSize {
let relativeSideSize = self.width / self.height
let newHeight = newWidth / relativeSideSize
return CGSize.init(width: newWidth, height: newHeight)
let newHeight = width / relativeSideSize
return CGSize.init(width: width, height: newHeight)
}
func resize(newHeight: CGFloat) -> CGSize {
func resize(height: CGFloat) -> CGSize {
let relativeSideSize = self.width / self.height
let newWidth = newHeight * relativeSideSize
return CGSize.init(width: newWidth, height: newHeight)
let newWidth = height * relativeSideSize
return CGSize.init(width: newWidth, height: height)
}
}
@@ -23,10 +23,20 @@ import Foundation
extension Date {
func formatted(as dateFormat: String) -> String {
func format(mask: String) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
dateFormatter.dateFormat = mask
return dateFormatter.string(from: self)
}
static func create(from value: String, mask: String = "dd.MM.yyyy HH:mm") -> Date? {
let formatter = DateFormatter()
formatter.dateFormat = mask
let date = formatter.date(from: value)
return date
}
func add(days: Int) -> Date {
return Calendar.current.date(byAdding: .day, value: days, to: self) ?? self
}
}
@@ -23,13 +23,13 @@ import Foundation
extension Strideable {
public mutating func setIfMore(when value: Self) {
mutating func setIfMore(when value: Self) {
if self > value {
self = value
}
}
public mutating func setIfFewer(when value: Self) {
mutating func setIfFewer(when value: Self) {
if self < value {
self = value
}
@@ -24,6 +24,10 @@ import UIKit
extension String {
var digits: String {
return components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
}
mutating func dropLast(substring: String) {
if self.hasSuffix(substring) {
self = String(dropLast(substring.count))
@@ -66,21 +70,32 @@ extension String {
return false
}
mutating func reduce(minimumFractionDigits: Int = 0, maximumFractionDigits: Int) {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = minimumFractionDigits
formatter.maximumFractionDigits = maximumFractionDigits
let int = Double(self)
if int != nil {
let number = NSNumber.init(value: int!)
if var newValue = formatter.string(from: number) {
newValue.replace(",", with: ".")
self = newValue
}
}
var isEmptyContent: Bool {
return (self.removeAllSpaces() == "")
}
var words: [String] {
return components(separatedBy: .punctuationCharacters).joined().components(separatedBy: .whitespaces)
}
func removePrefix(_ prefix: String) -> String {
guard self.hasPrefix(prefix) else { return self }
return String(self.dropFirst(prefix.count))
}
mutating func replace(_ replacingString: String, with newString: String) {
self = self.replacingOccurrences(of: replacingString, with: newString)
}
func replace(_ replacingString: String, with newString: String) -> String {
return self.replacingOccurrences(of: replacingString, with: newString)
}
func slice(from: String, to: String) -> String? {
return (range(of: from)?.upperBound).flatMap { substringFrom in
(range(of: to, range: substringFrom..<endIndex)?.lowerBound).map { substringTo in
String(self[substringFrom..<substringTo])
}
}
}
}
@@ -32,16 +32,17 @@ extension UIAlertController {
}
}
public static func show(title: String, message: String, buttonTitle: String, cancelButtonTitle: String? = nil, complection: @escaping ()->() = {}, on viewController: UIViewController) {
static func show(title: String? = nil, message: String? = nil, buttonTitle: String, cancelButtonTitle: String? = nil, complection: @escaping ()->() = {}, on viewController: UIViewController) {
let ac = UIAlertController(
title: title,
message: message,
preferredStyle: .alert
)
if cancelButtonTitle != nil {
if let cancelTitle = cancelButtonTitle {
ac.addAction(UIAlertAction.init(
title: cancelButtonTitle!,
title: cancelTitle,
style: UIAlertAction.Style.cancel,
handler: nil)
)
@@ -57,17 +58,15 @@ extension UIAlertController {
viewController.present(ac, animated: true, completion: nil)
}
public static func сonfirm(title: String? = nil, message: String, buttonTitle: String, cancelButtonTitle: String, isDestructive: Bool = false, complection: @escaping (Bool)->(), on viewController: UIViewController) {
static func сonfirm(title: String? = nil, message: String? = nil, buttonTitle: String, cancelButtonTitle: String, isDestructive: Bool = false, complection: @escaping (Bool)->(), on viewController: UIViewController) {
let ac = UIAlertController(
title: title,
message: message,
preferredStyle: .actionSheet
)
var style = UIAlertAction.Style.default
if isDestructive {
style = .destructive
}
let style = isDestructive ? .destructive : UIAlertAction.Style.default
ac.addAction(UIAlertAction.init(
title: buttonTitle,
style: style,
@@ -76,11 +75,10 @@ extension UIAlertController {
}))
ac.addAction(UIAlertAction.init(
title: cancelButtonTitle,
style: UIAlertAction.Style.default,
style: UIAlertAction.Style.cancel,
handler: { (action) in
complection(false)
}))
viewController.present(ac, animated: true, completion: nil)
}
}
@@ -100,4 +98,11 @@ extension UIAlertController {
}
self.addAction(action)
}
func addCancelAction(title: String, complection: @escaping ()->() = {}) {
let action = UIAlertAction(title: title, style: .cancel) { (action) in
complection()
}
self.addAction(action)
}
}
@@ -60,12 +60,21 @@ extension UIButton {
extension UIButton {
func setTitle(_ title: String) {
func setTitle(_ title: String, color: UIColor? = nil) {
self.setTitle(title, for: .normal)
if let color = color {
self.setTitleColor(color)
}
}
func setTitleColor(_ color: UIColor) {
self.setTitleColorForNoramlAndHightlightedStates(color: color)
self.setTitleColor(color, for: .normal)
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
}
func setImage(_ image: UIImage) {
self.setImage(image, for: .normal)
self.imageView?.contentMode = .scaleAspectFit
}
func removeAllTargets() {
@@ -74,23 +83,18 @@ extension UIButton {
func showText(_ text: String, withComplection completion: (() -> Void)! = {}) {
let baseText = self.titleLabel?.text ?? " "
SPAnimation.animate(0.2,
animations: {
self.titleLabel?.alpha = 0
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
self.setTitle(text, for: .normal)
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 0
}, delay: 0.35,
withComplection: {
self.setTitle(baseText, for: .normal)
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
@@ -102,14 +106,11 @@ extension UIButton {
}
func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
SPAnimation.animate(0.2,
animations: {
self.titleLabel?.alpha = 0
SPAnimation.animate(0.3, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
self.setTitle(text, for: .normal)
SPAnimation.animate(0.2, animations: {
SPAnimation.animate(0.3, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
completion()
@@ -118,25 +119,18 @@ extension UIButton {
}
func hideContent(completion: (() -> Void)! = {}) {
SPAnimation.animate(0.2,
animations: {
self.titleLabel?.alpha = 0
SPAnimation.animate(0.25, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
completion()
})
}
func showContent(completion: (() -> Void)! = {}) {
SPAnimation.animate(0.2,
animations: {
self.titleLabel?.alpha = 1
SPAnimation.animate(0.25, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
completion()
})
}
func setTitleColorForNoramlAndHightlightedStates(color: UIColor) {
self.setTitleColor(color, for: .normal)
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
}
}
@@ -29,4 +29,28 @@ extension UICollectionView {
let visibleIndexPath = self.indexPathForItem(at: visiblePoint)
return visibleIndexPath
}
func lastRow(indexPath: IndexPath) -> Int {
return self.numberOfItems(inSection: indexPath.section) - 1
}
func isLastRow(indexPath: IndexPath) -> Bool {
return indexPath.row == self.lastRow(indexPath: indexPath)
}
func reloadData(animated: Bool, complection: @escaping ()->() = {}) {
if animated {
UIView.transition(
with: self,
duration: 0.3,
options: [.transitionCrossDissolve, UIView.AnimationOptions.beginFromCurrentState],
animations: {
self.reloadData()
}, completion: {(state) in
complection()
})
} else {
self.reloadData()
}
}
}
@@ -21,7 +21,7 @@
import UIKit
public extension UIColor {
extension UIColor {
convenience init(hex: String) {
var red: CGFloat = 0.0
@@ -0,0 +1,61 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
extension UIFont {
static func system(weight: FontWeight, size: CGFloat) -> UIFont {
return UIFont.systemFont(ofSize: size, weight: self.weight(for: weight))
}
private static func weight(for weight: FontWeight) -> UIFont.Weight {
switch weight {
case .ultraLight:
return UIFont.Weight.ultraLight
case .light:
return UIFont.Weight.light
case .medium:
return UIFont.Weight.medium
case .regular:
return UIFont.Weight.regular
case .bold:
return UIFont.Weight.bold
case .demiBold:
return UIFont.Weight.semibold
case .heavy:
return UIFont.Weight.heavy
default:
return UIFont.Weight.regular
}
}
enum FontWeight {
case regular
case medium
case light
case ultraLight
case heavy
case bold
case demiBold
case none
}
}
@@ -0,0 +1,43 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
extension UIImage {
func resize(width: CGFloat) -> UIImage {
let scale = width / self.size.width
let newHeight = self.size.height * scale
UIGraphicsBeginImageContext(CGSize(width: width, height: newHeight))
self.draw(in: CGRect(x: 0, y: 0, width: width, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
var alwaysOriginal: UIImage {
return self.withRenderingMode(.alwaysOriginal)
}
var alwaysTemplate: UIImage {
return self.withRenderingMode(.alwaysTemplate)
}
}
@@ -23,18 +23,13 @@ import UIKit
extension UIImageView {
public func setNativeStyle() {
func setNative() {
self.layer.borderWidth = 0.5
self.layer.borderColor = SPNativeStyleKit.Colors.midGray.cgColor
self.layer.borderColor = SPNativeColors.midGray.cgColor
self.layer.masksToBounds = true
}
public func removeNativeStyle() {
self.layer.borderWidth = 0
self.layer.borderColor = UIColor.clear.cgColor
}
public func downloadedFrom(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
func downloadedFrom(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
DispatchQueue.main.async {
self.contentMode = mode
}
@@ -52,7 +47,7 @@ extension UIImageView {
}.resume()
}
public func downloadedFrom(link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
func downloadedFrom(link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
guard let url = URL(string: link) else { return }
downloadedFrom(url: url, contentMode: mode, withComplection: complection)
}
@@ -21,7 +21,7 @@
import UIKit
public extension UILabel {
extension UILabel {
func setShadowOffsetForLetters(blurRadius: CGFloat = 0, widthOffset: CGFloat = 0, heightOffset: CGFloat, opacity: CGFloat) {
self.layer.shadowRadius = blurRadius
@@ -42,7 +42,7 @@ public extension UILabel {
self.setShadowOffsetForLetters(blurRadius: 0, widthOffset: 0, heightOffset: 0, opacity: 0)
}
func setCenteringAlignment() {
func setCenterAlignment() {
self.textAlignment = .center
self.baselineAdjustment = .alignCenters
}
@@ -53,4 +53,48 @@ public extension UILabel {
attributedText = NSAttributedString(string: textString, attributes: attrs)
}
}
func setLineSpacing(_ lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {
guard let labelText = self.text else { return }
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.lineHeightMultiple = lineHeightMultiple
let attributedString: NSMutableAttributedString
if let labelattributedText = self.attributedText {
attributedString = NSMutableAttributedString(attributedString: labelattributedText)
} else {
attributedString = NSMutableAttributedString(string: labelText)
}
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
self.attributedText = attributedString
}
func setFormat(text: String, positions: [FormatPosition], font: UIFont, textColor: UIColor, backgroundColor: UIColor = .clear) {
let title = NSMutableAttributedString.init(string: text)
for position in positions {
title.addAttributes(
[
NSAttributedString.Key.backgroundColor : backgroundColor,
NSAttributedString.Key.foregroundColor : textColor,
NSAttributedString.Key.font : font
],
range: NSRange.init(location: position.start, length: position.length)
)
}
self.attributedText = title
}
struct FormatPosition {
var start: Int
var length: Int
}
}
@@ -25,7 +25,11 @@ extension UINavigationController {
static var elementsColor: UIColor {
get {
return UINavigationBar.appearance().tintColor
if UINavigationBar.appearance().tintColor != nil {
return UINavigationBar.appearance().tintColor
} else {
return SPNativeColors.blue
}
}
set {
UINavigationBar.appearance().tintColor = newValue
@@ -19,11 +19,11 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Foundation
import UIKit
extension Collection {
extension UIScrollView {
subscript (safe index: Index) -> Element? {
return indices.contains(index) ? self[index] : nil
func stopScrolling() {
self.setContentOffset(self.contentOffset, animated: false)
}
}
@@ -0,0 +1,52 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
extension UITabBarController {
static var elementsColor: UIColor {
get {
if UITabBar.appearance().tintColor != nil {
return UITabBar.appearance().tintColor
} else {
return SPNativeColors.blue
}
}
set {
UINavigationBar.appearance().tintColor = newValue
}
}
func addTabBarItem(title: String, image: UIImage, selectedImage: UIImage? = nil, controller: UIViewController) {
let tabBarItem = UITabBarItem(
title: title,
image: image,
selectedImage: selectedImage ?? image
)
controller.tabBarItem = tabBarItem
if self.viewControllers == nil { self.viewControllers = [controller] }
else { self.viewControllers?.append(controller) }
}
}
@@ -36,50 +36,24 @@ extension UITableView {
}
var lastSectionWithRows: Int? {
if self.numberOfSections == 0 {
return nil
}
if self.numberOfSections == 0 { return nil }
var section = self.numberOfSections - 1
if section < 0 {
return nil
}
if section < 0 { return nil }
while section >= 0 {
if self.numberOfRows(inSection: section) != 0 {
return section
}
if self.numberOfRows(inSection: section) != 0 { return section }
section -= 1
}
return nil
}
var firstSectionWithRows: Int? {
if self.numberOfSections == 0 {
return nil
}
if self.numberOfSections == 0 { return nil }
var section = 0
if section > self.numberOfSections - 1 {
return nil
}
if section > self.numberOfSections - 1 { return nil }
while section <= (self.numberOfSections - 1) {
if self.numberOfRows(inSection: section) != 0 {
return section
}
if self.numberOfRows(inSection: section) != 0 { return section }
section += 1
}
return nil
}
}
@@ -23,7 +23,7 @@ import UIKit
extension UITableViewCell {
public var accessoryView: UIView? {
var accessoryView: UIView? {
return subviews.compactMap { $0 as? UIButton }.first
}
@@ -33,12 +33,12 @@ extension UITableViewCell {
}
set {
let backgroundView = UIView()
backgroundView.backgroundColor = SPNativeStyleKit.Colors.customGray
backgroundView.backgroundColor = SPNativeColors.customGray
self.selectedBackgroundView = backgroundView
}
}
public func highlight() {
func highlight() {
self.setHighlighted(true, animated: false)
self.setHighlighted(false, animated: true)
}
@@ -39,7 +39,6 @@ extension UIViewController {
}
}
//MARK: - Keyboard
extension UIViewController {
func dismissKeyboardWhenTappedAround() {
@@ -53,16 +52,13 @@ extension UIViewController {
}
}
//MARK: - Add image to Library
extension UIViewController {
func save(image: UIImage) {
if SPPermission.isAllow(.photoLibrary) {
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
} else {
SPPermission.request(.photoLibrary) {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
}
print("Saving image error. Not allowed permission")
}
}
@@ -107,7 +103,6 @@ extension UIViewController {
}
}
//MARK: - Navigation Bar
extension UIViewController {
func setPrefersLargeNavigationTitle(_ title: String, smallScreenToSmallBar: Bool = true) {
@@ -129,31 +124,40 @@ extension UIViewController {
switch style {
case .large:
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .always
}
case .small:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
}
case .stork:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
case .noContent:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
}
}
}
extension UIViewController {
var topSafeArea: CGFloat {
return self.view.topSafeArea
}
var bottomSafeArea: CGFloat {
return self.view.bottomSafeArea
var safeArea: UIEdgeInsets {
if #available(iOS 11.0, *) {
return self.view.safeAreaInsets
} else {
return UIEdgeInsets.zero
}
}
var navigationBarHeight: CGFloat {
return self.navigationController?.navigationBar.frame.height ?? 0
}
var statusBarHeight: CGFloat {
static var statusBarHeight: CGFloat {
return UIApplication.shared.statusBarFrame.height
}
}
@@ -21,49 +21,38 @@
import UIKit
// MARK: - layout
public extension UIView {
extension UIView {
var topSafeArea: CGFloat {
var topSafeArea: CGFloat = 0
if #available(iOS 11.0, *) {
topSafeArea = self.safeAreaInsets.top
var viewController: UIViewController? {
get {
if let nextResponder = self.next as? UIViewController { return nextResponder }
else if let nextResponder = self.next as? UIView { return nextResponder.viewController }
else { return nil }
}
return topSafeArea
}
}
extension UIView {
var bottomSafeArea: CGFloat {
var bottomSafeArea: CGFloat = 0
var safeArea: UIEdgeInsets {
if #available(iOS 11.0, *) {
bottomSafeArea = self.safeAreaInsets.bottom
return self.safeAreaInsets
} else{
return UIEdgeInsets.zero
}
return bottomSafeArea
}
func setHeight(_ height: CGFloat) {
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.width, height: height)
func setBounds(_ view: UIView, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
self.setBounds(view.bounds, withWidthFactor: widthFactor, maxWidth: maxWidth, withHeightFactor: heightFactor, maxHeight: maxHeight, withCentering: withCentering)
}
func setWidth(_ width: CGFloat) {
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: width, height: self.frame.height)
}
func setEqualsFrameFromBounds(_ view: UIView, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
self.setEqualsFrameFromBounds(view.bounds, withWidthFactor: widthFactor, maxWidth: maxWidth, withHeightFactor: heightFactor, maxHeight: maxHeight, withCentering: withCentering)
}
func setEqualsFrameFromBounds(_ bounds: CGRect, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
func setBounds(_ bounds: CGRect, withWidthFactor widthFactor: CGFloat = 1, maxWidth: CGFloat? = nil, withHeightFactor heightFactor: CGFloat = 1, maxHeight: CGFloat? = nil, withCentering: Bool = false) {
var width = bounds.width * widthFactor
if maxWidth != nil {
width.setIfMore(when: maxWidth!)
}
if maxWidth != nil { width.setIfMore(when: maxWidth!) }
var height = bounds.height * heightFactor
if maxHeight != nil {
height.setIfMore(when: maxHeight!)
}
if maxHeight != nil { height.setIfMore(when: maxHeight!) }
self.frame = CGRect.init(x: 0, y: 0, width: width, height: height)
@@ -73,28 +62,20 @@ public extension UIView {
}
}
func setEqualsBoundsFromSuperview(customWidth: CGFloat? = nil, customHeight: CGFloat? = nil) {
if self.superview == nil {
return
}
func setSuperviewBounds(customWidth: CGFloat? = nil, customHeight: CGFloat? = nil) {
if self.superview == nil { return }
self.frame = CGRect.init(origin: CGPoint.zero, size: self.superview!.frame.size)
if customWidth != nil {
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: customWidth!, height: self.frame.height))
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: customWidth!, height: self.superview!.frame.height))
}
if customHeight != nil {
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: self.frame.width, height: customHeight!))
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: self.superview!.frame.width, height: customHeight!))
}
}
func resize(newWidth width: CGFloat) {
func resize(width: CGFloat) {
let relativeFactor = self.frame.width / self.frame.height
if relativeFactor.isNaN {
return
}
if relativeFactor.isNaN { return }
self.frame = CGRect.init(
x: self.frame.origin.x,
y: self.frame.origin.y,
@@ -103,11 +84,9 @@ public extension UIView {
)
}
func resize(newHeight height: CGFloat) {
func resize(height: CGFloat) {
let relativeFactor = self.frame.width / self.frame.height
if relativeFactor.isNaN {
return
}
if relativeFactor.isNaN { return }
self.frame = CGRect.init(
x: self.frame.origin.x,
y: self.frame.origin.y,
@@ -116,19 +95,23 @@ public extension UIView {
)
}
func setXCenteringFromSuperview() {
func setYCenter() {
self.center.y = (self.superview?.frame.height ?? 0) / 2
}
func setXCenter() {
self.center.x = (self.superview?.frame.width ?? 0) / 2
}
func setToCenterInSuperview() {
func setToCenter() {
self.center = CGPoint.init(x: ((self.superview?.frame.width) ?? 0) / 2, y: ((self.superview?.frame.height) ?? 0) / 2)
}
}
public extension UIView {
extension UIView {
func setParalax(amountFactor: CGFloat) {
let amount = self.frame.minSideSize * amountFactor
let amount = self.frame.minSide * amountFactor
self.setParalax(amount: amount)
}
@@ -148,29 +131,19 @@ public extension UIView {
}
}
// MARK: - convertToImage
public extension UIView {
func convertToImage() -> UIImage {
return UIImage.drawFromView(view: self)
}
}
// MARK: - gradeView
public extension UIView {
extension UIView {
func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
let gradeView = UIView.init()
gradeView.alpha = 0
self.addSubview(gradeView)
SPConstraintsAssistent.setEqualSizeConstraint(gradeView, superVuew: self)
SPConstraints.setEqualSizeSuperview(for: gradeView)
gradeView.alpha = alpha
gradeView.backgroundColor = color
return gradeView
}
}
// MARK: - shadow
extension UIView {
func setShadow(
@@ -188,14 +161,14 @@ extension UIView {
let xTranslation = (self.frame.width - shadowWidth) / 2 + (self.frame.width * xTranslationFactor)
let yTranslation = (self.frame.height - shadowHeight) / 2 + (self.frame.height * yTranslationFactor)
let cornerRadius = self.frame.minSideSize * cornerRadiusFactor
let cornerRadius = self.frame.minSide * cornerRadiusFactor
let shadowPath = UIBezierPath.init(
roundedRect: CGRect.init(x: xTranslation, y: yTranslation, width: shadowWidth, height: shadowHeight),
cornerRadius: cornerRadius
)
let blurRadius = self.frame.minSideSize * blurRadiusFactor
let blurRadius = self.frame.minSide * blurRadiusFactor
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize.zero
@@ -249,7 +222,6 @@ extension UIView {
}
}
// MARK: - animation
extension UIView {
func addCornerRadiusAnimation(to: CGFloat, duration: CFTimeInterval) {
@@ -276,9 +248,12 @@ extension UIView {
self.isHidden = true
})
}
func removeAllAnimations() {
self.layer.removeAllAnimations()
}
}
// MARK: - corner radius
extension UIView {
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
@@ -289,6 +264,6 @@ extension UIView {
}
func round() {
self.layer.cornerRadius = self.frame.minSideSize / 2
self.layer.cornerRadius = self.frame.minSide / 2
}
}
@@ -21,15 +21,16 @@
import UIKit
struct SPApp {
extension UIVisualEffectView {
static var udid: String? {
return UIDevice.current.identifierForVendor?.uuidString
convenience init(style: UIBlurEffect.Style) {
let effect = UIBlurEffect(style: style)
self.init(effect: effect)
}
static var displayName: String? {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
convenience init(vibrancy style: UIBlurEffect.Style) {
let effect = UIBlurEffect(style: style)
let vibrancyEffect = UIVibrancyEffect(blurEffect: effect)
self.init(effect: vibrancyEffect)
}
private init() {}
}
@@ -21,22 +21,34 @@
import UIKit
class SPLayout {
struct SPLayout {
static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat, heightFactor: CGFloat, maxHeight: CGFloat, relativeSideFactor: CGFloat, from relativeSize: CGSize) -> CGSize {
static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat?, heightFactor: CGFloat, maxHeight: CGFloat?, relativeSideFactor: CGFloat?, from size: CGSize) -> CGSize {
var widthArea = relativeSize.width * widthFactor
var heightArea = relativeSize.height * heightFactor
var widthArea = size.width * widthFactor
var heightArea = size.height * heightFactor
widthArea.setIfMore(when: maxWidth)
heightArea.setIfMore(when: maxHeight)
if let maxWidth = maxWidth {
widthArea.setIfMore(when: maxWidth)
}
if let maxHeight = maxHeight {
heightArea.setIfMore(when: maxHeight)
}
var prepareWidth = widthArea
var prepareHeight = widthArea / relativeSideFactor
if prepareHeight > heightArea {
prepareHeight = heightArea
prepareWidth = heightArea * relativeSideFactor
var prepareHeight = heightArea
if let relativeSideFactor = relativeSideFactor {
prepareHeight = widthArea / relativeSideFactor
if prepareHeight > heightArea {
prepareHeight = heightArea
prepareWidth = heightArea * relativeSideFactor
}
}
return CGSize.init(width: prepareWidth, height: prepareHeight)
}
private init() {}
}
@@ -20,32 +20,22 @@
// SOFTWARE.
import UIKit
import LocalAuthentication
extension CGRect {
struct SPLocalAuthentication {
var bottomXPosition: CGFloat {
get {
return self.origin.x + self.width
}
set {
self.origin.x = newValue - self.width
static var isEnable: Bool {
let context = LAContext()
var error: NSError?
return context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)
}
static func request(reason: String, complecton: @escaping (Bool)->()) {
let context = LAContext()
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, error in
DispatchQueue.main.async { complecton(success) }
}
}
var bottomYPosition: CGFloat {
get {
return self.origin.y + self.height
}
set {
self.origin.y = newValue - self.height
}
}
var minSideSize: CGFloat {
return min(self.width, self.height)
}
var isWidthLessThanHeight: Bool {
return self.width < self.height
}
private init() {}
}
@@ -28,7 +28,7 @@ struct SPMail {
return MFMailComposeViewController.canSendMail()
}
static func openMailApp(to email: String, subject: String? = nil, body: String? = nil) {
static func openApp(to email: String, subject: String? = nil, body: String? = nil) {
let parametrs = "mailto:\(email)?subject=\(subject ?? "")&body=\(body ?? "")".addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
if parametrs != nil {
@@ -40,7 +40,7 @@ struct SPMail {
}
}
static func mailDialog(to email: String, subject: String? = nil, body: String? = nil, on viewController: UIViewController) {
static func dialog(to email: String, subject: String? = nil, body: String? = nil, on viewController: UIViewController) {
let mailVC = MFMailComposeViewController()
mailVC.mailComposeDelegate = SPMailSingltone.sharedInstance
@@ -69,7 +69,6 @@ struct SPMail {
private init() {}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}
@@ -0,0 +1,41 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
enum SPNativeColors {
static let red = UIColor.init(hex: "FF3B30")
static let orange = UIColor.init(hex: "FF9500")
static let yellow = UIColor.init(hex: "FFCC00")
static let green = UIColor.init(hex: "4CD964")
static let tealBlue = UIColor.init(hex: "5AC8FA")
static let blue = UIColor.init(hex: "007AFF")
static let purple = UIColor.init(hex: "5856D6")
static let pink = UIColor.init(hex: "FF2D55")
static let white = UIColor.init(hex: "FFFFFF")
static let customGray = UIColor.init(hex: "EFEFF4")
static let lightGray = UIColor.init(hex: "E5E5EA")
static let lightGray2 = UIColor.init(hex: "D1D1D6")
static let midGray = UIColor.init(hex: "C7C7CC")
static let gray = UIColor.init(hex: "8E8E93")
static let black = UIColor.init(hex: "000000")
}
@@ -0,0 +1,88 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
import UserNotifications
struct SPLocalNotification {
var identificator: String? = nil
var text: String
var title: String? = nil
var badge: Int = 0
var timeInterval: TimeInterval
var soundEnabled: Bool = true
var category: SPLocalNotificationCategory? = nil
init(after timeInterval: TimeInterval, text: String) {
self.text = text
self.timeInterval = timeInterval
}
func add() {
let identificator = self.identificator ?? "\(self.timeInterval)\(self.text)\(Int.random(min: 0, max: 1000))"
let notification = UNNotificationRequest(
identifier: identificator,
content: self.content,
trigger: self.trigger
)
let center = UNUserNotificationCenter.current()
center.add(notification) { (error) in
if let error = error {
print("SPLocalNotification - \(error)")
}
}
}
private var content: UNMutableNotificationContent {
let content = UNMutableNotificationContent()
content.body = self.text
content.title = self.title ?? ""
content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + self.badge)
content.sound = self.soundEnabled ? UNNotificationSound.default : nil
if let category = self.category {
if #available(iOS 12.0, *) {
let notificationCategory = UNNotificationCategory(identifier: category.identifier, actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: nil, categorySummaryFormat: category.summary, options: [])
UNUserNotificationCenter.current().setNotificationCategories([notificationCategory])
content.categoryIdentifier = notificationCategory.identifier
}
}
return content
}
private var trigger: UNTimeIntervalNotificationTrigger {
return UNTimeIntervalNotificationTrigger(timeInterval: self.timeInterval, repeats: false)
}
}
struct SPLocalNotificationCategory {
var identifier: String
var summary: String
static var countSymbol: String {
return "%u"
}
}
@@ -21,8 +21,9 @@
import UIKit
public extension String {
public static func random(count: Int) -> String {
extension String {
static func random(count: Int) -> String {
let strings = [
"В доме кардинала от меня не было тайн; не раз видел я, как он усердно перелистывает старинные книги и жадно роется в пыли фамильных рукописей. Когда я как-то упрекнул его за бесполезные бессонные ночи, после которых он впадал в болезненное уныние, он взглянул на меня с горькой улыбкой и раскрыл передо мною историю города Рима. В этой книге, в двадцатой главе жизнеописания папы Александра Шестого, я прочел следующие строки, навсегда оставшиеся в моей памяти",
"По этому поводу между отцом и сыном завязался спор. Цезарь считал, что достаточно применить одно из тех средств, которые он всегда держал наготове для своих ближайших друзей, а именно: пресловутый ключ, которым то одного, то другого просили отпереть некий шкаф. На ключе был крохотный железный шип – недосмотр слесаря. Каждый, кто трудился над тугим замком, накалывал себе палец и на другой день умирал. Был еще перстень с львиной головой, который Цезарь надевал, когда хотел пожать руку той или иной особе. Лев впивался в кожу этих избранных рук, и через сутки наступала смерть.",
@@ -33,58 +34,47 @@ public extension String {
}
}
public extension Bool {
public static func random() -> Bool {
return arc4random_uniform(2) == 0
}
}
public extension Int {
public static func random(_ n: Int) -> Int {
return Int(arc4random_uniform(UInt32(n)))
extension Int {
static func random() -> Int {
return Int.random(in: 0...Int.max)
}
public static func random(min: Int, max: Int) -> Int {
return Int(arc4random_uniform(UInt32(max - min - 1))) + min
static func random(min: Int, max: Int) -> Int {
return Int.random(in: min...max)
}
}
public extension Double {
public static func random() -> Double {
return Double(arc4random()) / 0xFFFFFFFF
extension Double {
static func random() -> Double {
return Double.random(in: 0...Double.greatestFiniteMagnitude)
}
public static func random(min: Double, max: Double) -> Double {
return Double.random() * (max - min) + min
static func random(min: Double, max: Double) -> Double {
return Double.random(in: min...max)
}
}
public extension Float {
public static func random() -> Float {
return Float(arc4random()) / 0xFFFFFFFF
extension Float {
static func random() -> Float {
return Float.random(in: 0...Float.greatestFiniteMagnitude)
}
public static func random(min: Float, max: Float) -> Float {
return Float.random() * (max - min) + min
static func random(min: Float, max: Float) -> Float {
return Float.random(in: min...max)
}
}
public extension CGFloat {
public static func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
extension CGFloat {
static func random() -> CGFloat {
return CGFloat.random(in: 0...CGFloat.greatestFiniteMagnitude)
}
public static func random(min: CGFloat, max: CGFloat) -> CGFloat {
return CGFloat.random() * (max - min) + min
}
}
public extension Collection {
/// Return a copy of `self` with its elements shuffled
func shuffle() -> [Iterator.Element] {
var list = Array(self)
list.shuffleInPlace()
return list
static func random(min: CGFloat, max: CGFloat) -> CGFloat {
return CGFloat.random(in: min...max)
}
}
@@ -95,10 +85,10 @@ extension Collection where Index == Int {
}
}
public extension MutableCollection where Index == Int {
/// Shuffle the elements of `self` in-place.
extension MutableCollection where Index == Int {
mutating func shuffleInPlace() {
// empty and single-element collections don't shuffle
if count < 2 { return }
for i in startIndex ..< endIndex - 1 {
@@ -27,13 +27,13 @@ extension SPShadow {
private init() {}
public static func setFor(label: UILabel) {
static func setFor(label: UILabel) {
var offset = label.frame.height * 0.03
offset.setIfMore(when: 1)
label.setShadowOffsetForLetters(heightOffset: offset, opacity: 0.35)
}
public static func setFor(view: UIView) {
static func setFor(view: UIView) {
let xTranslationFactor: CGFloat = 0
let yTranslationFactor: CGFloat = 0.18
@@ -66,7 +66,7 @@ extension SPShadow {
yTranslation = view.frame.height + maxBottomSpace - shadowHeight
}
var blurRadius = view.frame.minSideSize * blurRadiusFactor
var blurRadius = view.frame.minSide * blurRadiusFactor
blurRadius.setIfMore(when: 10)
blurRadius.setIfFewer(when: 7)
@@ -21,9 +21,9 @@
import UIKit
public struct SPShare {
struct SPShare {
public struct Native {
struct Native {
static func share(text: String? = nil, fileNames: [String] = [], images: [UIImage] = [], complection: ((_ isSharing: Bool)->())? = nil, sourceView: UIView, on viewController: UIViewController) {
@@ -24,11 +24,7 @@ import UIKit
class SPInstagram {
static var isSetApp: Bool {
if UIApplication.shared.canOpenURL(URL(string: "instagram://user?username=test")!) {
return true
} else {
return false
}
return UIApplication.shared.canOpenURL(URL(string: "instagram://user?username=test")!)
}
static func openPost(id: String) {
@@ -38,7 +34,7 @@ class SPInstagram {
if UIApplication.shared.canOpenURL(instagramUrl!) {
UIApplication.shared.open(instagramUrl!, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
} else {
SPOpener.Link.redirectToBrowserAndOpen(link: safariURL)
SPApp.open(link: safariURL.absoluteString)
}
}
@@ -49,14 +45,13 @@ class SPInstagram {
if UIApplication.shared.canOpenURL(instagramUrl!) {
UIApplication.shared.open(instagramUrl!, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
} else {
SPOpener.Link.redirectToBrowserAndOpen(link: safariURL)
SPApp.open(link: safariURL.absoluteString)
}
}
private init() {}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}
@@ -24,11 +24,7 @@ import UIKit
class SPTelegram {
static var isSetApp: Bool {
if UIApplication.shared.canOpenURL(URL(string: "tg://msg?text=test")!) {
return true
} else {
return false
}
return UIApplication.shared.canOpenURL(URL(string: "tg://msg?text=test")!)
}
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
@@ -47,7 +43,12 @@ class SPTelegram {
static func joinChannel(id: String) {
let url = "https://t.me/joinchat/\(id)"
SPOpener.Link.redirectToBrowserAndOpen(link: url)
SPApp.open(link: url)
}
static func joinChat(id: String) {
let url = "https://t.me/joinchat/\(id)"
SPApp.open(link: url)
}
static func openBot(username: String) {
@@ -56,13 +57,17 @@ class SPTelegram {
username.removeFirst()
}
let url = "https://telegram.me/\(username)"
SPOpener.Link.redirectToBrowserAndOpen(link: url)
SPApp.open(link: url)
}
static func openDialog(username: String) {
let url = "https://t.me/\(username)"
SPApp.open(link: url)
}
private init() {}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}
@@ -24,11 +24,7 @@ import UIKit
class SPTwitter {
static var isSetApp: Bool {
if UIApplication.shared.canOpenURL(URL(string: "twitter://post?message=test")!) {
return true
} else {
return false
}
return UIApplication.shared.canOpenURL(URL(string: "twitter://post?message=test")!)
}
static func share(text: String, complection: @escaping (_ isOpened: Bool)->() = {_ in }) {
@@ -48,7 +44,6 @@ class SPTwitter {
private init() {}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}

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