Compare commits

...

341 Commits

Author SHA1 Message Date
Ivan Vorobei 196c96a5d7 Update email 2020-03-16 00:21:40 +03:00
Ivan Vorobei dae24780b0 Update email 2020-03-05 12:02:18 +03:00
Ivan Vorobei ac33fc25e3 Update README.md 2020-01-18 16:18:24 +03:00
Ivan Vorobei a8f90dae76 Update README.md 2019-11-18 16:32:23 +03:00
Ivan Vorobei 1f34d8b641 Update README.md 2019-11-18 03:19:05 +03:00
Ivan Vorobei 5f6ea44f44 Update README.md 2019-11-18 03:18:39 +03:00
Ivan Vorobei c5a7709bef Update README.md 2019-11-18 03:16:53 +03:00
Ivan Vorobei 59f004625b Update README.md 2019-10-27 00:01:28 +03:00
Ivan Vorobei b9f02d317e Update to 1.7.9 2019-08-17 16:30:00 +03:00
Ivan Vorobei 29d3fe128e Fix removing animation from arrow layer 2019-08-17 16:27:22 +03:00
Ivan Vorobei 0463bb7a8e Update to 1.7.8
Fix bug with indicator when device rotation. Add fix indicator style. Need set `indicatorMode` in transition delegate.
2019-08-17 16:23:45 +03:00
Ivan Vorobei 12f8e2bc0d Update Package.swift 2019-08-06 11:29:03 +03:00
Ivan Vorobei 578b3dbb41 Update Package.swift 2019-08-06 11:03:56 +03:00
Ivan Vorobei 223ec2d2c0 Update to 1.7.7
Fix memory leak when use UINavigationController
2019-08-05 23:19:16 +03:00
Ivan Vorobei a0ca7e9e2a Merge pull request #92 from Moriquendi/master
#88 Prevent controller leaking when embedded in UINavigationController
2019-08-05 23:12:42 +03:00
Michał Śmiałko f0a1914145 Check if controller's modalPresentationStyle is custom before accessing presentationController. 2019-08-05 14:33:44 +02:00
Ivan Vorobei 4355f5eb09 Update UserInterfaceState.xcuserstate 2019-07-22 20:34:29 +03:00
Ivan Vorobei 6b080f503d Update README.md 2019-07-22 11:20:40 +03:00
Ivan Vorobei c23ed83bf0 Update UserInterfaceState.xcuserstate 2019-07-22 11:20:00 +03:00
Ivan Vorobei 8b1c77d743 Update README.md 2019-07-18 17:30:44 +03:00
Ivan Vorobei 915ef85cff Update README.md 2019-07-18 17:26:46 +03:00
Ivan Vorobei dc1050be2a Update README.md 2019-07-18 13:04:34 +03:00
Ivan Vorobei 99ba8bf657 Update README.md 2019-07-18 13:03:33 +03:00
Ivan Vorobei 2b7e4198cf Update SPStorkController.podspec 2019-07-16 20:29:33 +03:00
Ivan Vorobei 0969cceac8 Update close method for UIAccessibility
Also update example project
2019-07-16 20:29:24 +03:00
Ivan Vorobei f349dea851 Merge pull request #81 from irskep/irskep-accessibility
Improve accessibility (fix #80)
2019-07-16 20:20:00 +03:00
Ivan Vorobei b8e8f8d0ce Update to 1.7.5
Fix completion for confirmation.
2019-07-11 19:21:29 +03:00
Ivan Vorobei ee5f9ee9de Update to 1.7.4
Add `dismissWithConfirmation` method for custom buttons.
2019-07-11 01:35:56 +03:00
Ivan Vorobei 8348cd6396 Update to 1.7.2
Fix bugs with events delegate.
2019-07-11 01:17:48 +03:00
Ivan Vorobei 9f4c1be56d Update to 1.7.1
Add confirmation to dismiss by scroll. Change `SPStorkControllerConfirmDelegate` to required methods. Update example.
2019-07-10 21:46:53 +03:00
Ivan Vorobei de779aebef Update example 2019-07-10 17:44:59 +03:00
Ivan Vorobei 1785822242 Update to 1.7
Add confirmation fitures.
2019-07-10 17:43:37 +03:00
Ivan Vorobei 1514ea5481 Update README.md 2019-07-06 16:38:20 +03:00
Ivan Vorobei ec81b9f5dd Update README.md 2019-07-06 16:37:47 +03:00
Ivan Vorobei 7381066b36 Update to 1.6.9 2019-07-05 23:33:54 +03:00
Ivan Vorobei bc998622eb Update to 1.6.8
Fix scroll indicator position
2019-07-05 23:21:44 +03:00
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
Steve Johnson 97c8e8aa32 Fix indentation 2019-07-04 14:12:16 -07:00
Steve Johnson 7be93d548e Improve accessibility 2019-07-04 14:11:08 -07: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
212 changed files with 6756 additions and 4401 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
+38
View File
@@ -1 +1,39 @@
# 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/
.swiftpm/
# 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>
+58
View File
@@ -0,0 +1,58 @@
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()
transitionDelegate.storkDelegate = self
transitionDelegate.confirmDelegate = modal
modal.transitioningDelegate = transitionDelegate
modal.modalPresentationStyle = .custom
self.present(modal, animated: true, completion: nil)
}
@objc func presentModalTableViewController() {
let modal = ModalTableViewController()
let transitionDelegate = SPStorkTransitioningDelegate()
transitionDelegate.storkDelegate = self
transitionDelegate.confirmDelegate = modal
modal.transitioningDelegate = transitionDelegate
modal.modalPresentationStyle = .custom
self.present(modal, animated: true, completion: nil)
}
}
extension Controller: SPStorkControllerDelegate {
func didDismissStorkByTap() {
print("SPStorkControllerDelegate - didDismissStorkByTap")
}
func didDismissStorkBySwipe() {
print("SPStorkControllerDelegate - didDismissStorkBySwipe")
}
}
@@ -0,0 +1,30 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public enum SPFakeBarNavigationStyle {
case large
case small
case stork
case noContent
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,9 +21,9 @@
import UIKit
public class SPFakeBarView: UIView {
open class SPFakeBarView: UIView {
public var style: SPNavigationTitleStyle = . small {
public var style: SPFakeBarNavigationStyle = .small {
didSet {
self.updateStyle()
}
@@ -32,7 +32,7 @@ public class SPFakeBarView: UIView {
private var settedHeight: CGFloat = 0
public var height: CGFloat {
get {
return (self.settedHeight) + (self.addStatusBarHeight ? UIViewController.statusBarHeight : 0)
return (self.settedHeight) + (self.addStatusBarHeight ? UIApplication.shared.statusBarFrame.height : 0)
}
set {
self.settedHeight = newValue
@@ -47,10 +47,27 @@ public class SPFakeBarView: UIView {
}
}
public var elementsColor: UIColor = UINavigationController.elementsColor {
public var elementsColor: UIColor = SPFakeBarView.navigationElementsColor {
didSet {
self.leftButton.setTitleColor(self.elementsColor)
self.rightButton.setTitleColor(self.elementsColor)
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
}
}
}
@@ -59,21 +76,25 @@ public class SPFakeBarView: UIView {
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?
private let blurView = UIVisualEffectView.init(style: .extraLight)
private let separatorView = UIView()
public init(style: SPNavigationTitleStyle) {
public init(style: SPFakeBarNavigationStyle) {
super.init(frame: CGRect.zero)
self.style = style
self.commonInit()
}
public required init?(coder aDecoder: NSCoder) {
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.style = .small
self.commonInit()
@@ -90,7 +111,7 @@ public class SPFakeBarView: UIView {
self.blurView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
self.addSubview(self.separatorView)
self.separatorView.backgroundColor = UIColor.init(hex: "BFBFBF")
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
@@ -105,36 +126,40 @@ public class SPFakeBarView: UIView {
self.titleBottomConstraint?.isActive = true
self.addSubview(self.subtitleLabel)
self.subtitleLabel.textColor = UIColor.init(hex: "8E8E92")
self.subtitleLabel.font = UIFont.system(type: .DemiBold, size: 13)
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)
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.system(type: .DemiBold, size: 16)
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)
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.system(type: .DemiBold, size: 17)
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()
}
public override func layoutSubviews() {
override open func layoutSubviews() {
super.layoutSubviews()
self.setContraints()
}
@@ -160,7 +185,7 @@ public class SPFakeBarView: UIView {
private func updateStyle() {
switch self.style {
case .small:
if UIViewController.statusBarHeight == 44 {
if UIApplication.shared.statusBarFrame.height == 44 {
self.height = 88 - 44
self.titleBottomConstraint?.constant = -12
} else {
@@ -168,26 +193,29 @@ public class SPFakeBarView: UIView {
self.titleBottomConstraint?.constant = -12
}
self.addStatusBarHeight = true
self.titleLabel.font = UIFont.system(type: .DemiBold, size: 17)
self.titleLabel.setCenteringAlignment()
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.system(type: .DemiBold, size: 17)
self.titleLabel.setCenteringAlignment()
self.titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
self.titleLabel.textAlignment = .center
case .large:
if UIViewController.statusBarHeight == 44 {
if UIApplication.shared.statusBarFrame.height == 44 {
self.height = 140 - 44
self.titleBottomConstraint?.constant = -8
} else {
self.height = 112 - 20
self.height = 116 - 20
self.titleBottomConstraint?.constant = -4
}
self.addStatusBarHeight = true
self.titleLabel.font = UIFont.system(type: .Bold, size: 34)
self.titleLabel.font = UIFont.systemFont(ofSize: 34, weight: .bold)
self.titleLabel.textAlignment = .left
break
case .noContent:
self.height = 0
self.addStatusBarHeight = true
}
self.updateConstraints()
@@ -197,5 +225,26 @@ public class SPFakeBarView: UIView {
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 Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
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)
}
}
@@ -0,0 +1,29 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public enum SPStorkArrowMode {
case auto
case alwaysArrow
case alwaysLine
}
@@ -0,0 +1,29 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public enum SPStorkHapticMoments {
case willPresent
case willDismiss
case willDismissIfRelease
}
@@ -0,0 +1,29 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
@objc public protocol SPStorkControllerConfirmDelegate: class {
var needConfirm: Bool { get }
func confirm(_ completion: @escaping (_ isConfirmed: Bool)->())
}
@@ -0,0 +1,29 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
@objc public protocol SPStorkControllerDelegate: class {
@objc optional func didDismissStorkBySwipe()
@objc optional func didDismissStorkByTap()
}
+58 -15
View File
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,26 +21,80 @@
import UIKit
public struct SPStorkController {
public enum SPStorkController {
static public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let controller = self.controller(for: scrollView) {
if let presentationController = controller.presentationController as? SPStorkPresentationController {
if let presentationController = self.presentationController(for: controller) {
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 {
self.dismissWithConfirmation(controller: controller, completion: {
presentationController.storkDelegate?.didDismissStorkBySwipe?()
})
return
}
}
if presentationController.pan?.state != UIGestureRecognizer.State.changed {
presentationController.scrollViewDidScroll(translation)
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 func dismissWithConfirmation(controller: UIViewController, completion: (()->())?) {
if let controller = self.presentationController(for: controller) {
controller.dismissWithConfirmation(prepare: nil, completion: {
completion?()
})
}
}
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 presentationController(for controller: UIViewController) -> SPStorkPresentationController? {
guard controller.modalPresentationStyle == .custom else { return nil }
if let presentationController = controller.presentationController as? SPStorkPresentationController {
return presentationController
}
if let presentationController = controller.parent?.presentationController as? SPStorkPresentationController {
return presentationController
}
return nil
}
static private func controller(for view: UIView) -> UIViewController? {
var nextResponder = view.next
while nextResponder != nil && !(nextResponder! is UIViewController) {
@@ -48,15 +102,4 @@ public struct SPStorkController {
}
return nextResponder as? UIViewController
}
private init() {}
}
extension UIViewController {
var isPresentedAsStork: Bool {
return transitioningDelegate is SPStorkTransitioningDelegate
&& modalPresentationStyle == .custom
&& presentingViewController != nil
}
}
@@ -0,0 +1,34 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
public class SPStorkSegue: UIStoryboardSegue {
public var transitioningDelegate: SPStorkTransitioningDelegate?
override public func perform() {
transitioningDelegate = transitioningDelegate ?? SPStorkTransitioningDelegate()
destination.transitioningDelegate = transitioningDelegate
destination.modalPresentationStyle = .custom
super.perform()
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -29,8 +29,10 @@ final class SPStorkDismissingAnimationController: NSObject, UIViewControllerAnim
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),
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -23,12 +23,25 @@ import UIKit
class SPStorkPresentationController: UIPresentationController, UIGestureRecognizerDelegate {
var isSwipeToDismissEnabled: Bool = true
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 indicatorMode: SPStorkArrowMode = .auto
var customHeight: CGFloat? = nil
var transitioningDelegate: SPStorkTransitioningDelegate?
var pan: UIPanGestureRecognizer?
var translateForDismiss: CGFloat = 200
var hapticMoments: [SPStorkHapticMoments] = [.willDismissIfRelease]
var transitioningDelegate: SPStorkTransitioningDelegate?
weak var storkDelegate: SPStorkControllerDelegate?
weak var confirmDelegate: SPStorkControllerConfirmDelegate?
var pan: UIPanGestureRecognizer?
var tap: UITapGestureRecognizer?
private var closeButton = SPStorkCloseButton()
private var indicatorView = SPStorkIndicatorView()
private var gradeView: UIView = UIView()
private let snapshotViewContainer = UIView()
@@ -38,46 +51,101 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
private var snapshotViewTopConstraint: NSLayoutConstraint?
private var snapshotViewWidthConstraint: NSLayoutConstraint?
private var snapshotViewAspectRatioConstraint: NSLayoutConstraint?
var workConfirmation: Bool = false
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 var alpha: CGFloat {
return 0.51
}
private var cornerRadius: CGFloat {
return 10
}
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.topSpace * 2 / containerView.frame.height)
let factor = 1 - ((self.cornerRadius + 3) * 2 / containerView.frame.width)
return factor
}
private var feedbackGenerator = 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 additionTranslate = containerView.bounds.height - (self.customHeight ?? containerView.bounds.height)
let yOffset: CGFloat = self.topSpace + 13 + additionTranslate
return CGRect(x: 0, y: yOffset, width: containerView.bounds.width, height: containerView.bounds.height - yOffset)
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 }
let closeTitle = NSLocalizedString("Close", comment: "Close")
if self.showIndicator {
self.indicatorView.color = self.indicatorColor
let tap = UITapGestureRecognizer.init(target: self, action: #selector(self.tapIndicator))
tap.cancelsTouchesInView = false
self.indicatorView.addGestureRecognizer(tap)
self.indicatorView.accessibilityLabel = closeTitle
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.indicatorView.mode = self.indicatorMode
if UIAccessibility.isVoiceOverRunning {
let accessibleIndicatorOverlayButton = UIButton(type: .custom)
accessibleIndicatorOverlayButton.addTarget(self, action: #selector(self.tapIndicator), for: .touchUpInside)
accessibleIndicatorOverlayButton.accessibilityLabel = closeTitle
presentedView.addSubview(accessibleIndicatorOverlayButton)
accessibleIndicatorOverlayButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
accessibleIndicatorOverlayButton.leadingAnchor.constraint(equalTo: presentedView.leadingAnchor),
accessibleIndicatorOverlayButton.trailingAnchor.constraint(equalTo: presentedView.trailingAnchor),
accessibleIndicatorOverlayButton.topAnchor.constraint(equalTo: presentedView.topAnchor),
accessibleIndicatorOverlayButton.bottomAnchor.constraint(equalTo: self.indicatorView.bottomAnchor),
])
}
}
self.updateLayoutIndicator()
self.indicatorView.style = .arrow
self.gradeView.alpha = 0
self.closeButton.accessibilityLabel = closeTitle
if self.showCloseButton {
self.closeButton.addTarget(self, action: #selector(self.tapCloseButton), for: .touchUpInside)
presentedView.addSubview(self.closeButton)
}
self.updateLayoutCloseButton()
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
@@ -119,8 +187,9 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
containerView.insertSubview(snapshotView, aboveSubview: self.backgroundView)
snapshotView.frame = initialFrame
snapshotView.transform = transformForSnapshotView
snapshotView.alpha = self.alpha
snapshotView.alpha = 1 - self.alpha
snapshotView.layer.cornerRadius = self.cornerRadius
snapshotView.contentMode = .top
snapshotView.layer.masksToBounds = true
rootSnapshotView = snapshotView
@@ -143,6 +212,10 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
rootSnapshotView?.removeFromSuperview()
rootSnapshotRoundedView?.removeFromSuperview()
})
if self.hapticMoments.contains(.willPresent) {
self.feedbackGenerator.impactOccurred()
}
}
override func presentationTransitionDidEnd(_ completed: Bool) {
@@ -155,8 +228,14 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
self.updateSnapshotAspectRatio()
if self.isSwipeToDismissEnabled {
self.pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
if self.tapAroundToDismissEnabled {
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.tapArround))
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
@@ -170,7 +249,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
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)
@@ -199,6 +278,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
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
@@ -206,7 +286,7 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
let snapshotRoundedView = UIView()
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
snapshotRoundedView.layer.masksToBounds = true
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(1 - self.alpha)
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(self.alpha)
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
snapshotRoundedView.frame = initialFrame
snapshotRoundedView.transform = initialTransform
@@ -219,6 +299,9 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
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()
@@ -232,6 +315,8 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
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
@@ -241,19 +326,71 @@ class SPStorkPresentationController: UIPresentationController, UIGestureRecogniz
extension SPStorkPresentationController {
@objc func tapIndicator() {
self.dismissWithConfirmation(prepare: nil, completion: {
self.storkDelegate?.didDismissStorkByTap?()
})
}
@objc func tapArround() {
self.dismissWithConfirmation(prepare: nil, completion: {
self.storkDelegate?.didDismissStorkByTap?()
})
}
@objc func tapCloseButton() {
self.dismissWithConfirmation(prepare: nil, completion: {
self.storkDelegate?.didDismissStorkByTap?()
})
}
public func dismissWithConfirmation(prepare: (()->())?, completion: (()->())?) {
let dismiss = {
self.presentingViewController.view.endEditing(true)
self.presentedViewController.view.endEditing(true)
self.presentedViewController.dismiss(animated: true, completion: {
completion?()
})
}
guard let confirmDelegate = self.confirmDelegate else {
dismiss()
return
}
if self.workConfirmation { return }
if confirmDelegate.needConfirm {
prepare?()
self.workConfirmation = true
confirmDelegate.confirm({ (isConfirmed) in
self.workConfirmation = false
self.afterReleaseDismissing = false
if isConfirmed {
dismiss()
}
})
} else {
dismiss()
}
}
@objc func handlePan(gestureRecognizer: UIPanGestureRecognizer) {
guard gestureRecognizer.isEqual(pan), self.isSwipeToDismissEnabled else { return }
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
if self.isSwipeToDismissEnabled {
let translation = gestureRecognizer.translation(in: presentedView)
let translation = gestureRecognizer.translation(in: presentedView)
if self.swipeToDismissEnabled {
self.updatePresentedViewForTranslation(inVerticalDirection: translation.y)
} else {
gestureRecognizer.setTranslation(.zero, in: presentedView)
@@ -261,9 +398,8 @@ extension SPStorkPresentationController {
case .ended:
self.workGester = false
let translation = gestureRecognizer.translation(in: presentedView).y
if translation >= 240 {
presentedViewController.dismiss(animated: true, completion: nil)
} else {
let toDefault = {
self.indicatorView.style = .arrow
UIView.animate(
withDuration: 0.6,
@@ -277,11 +413,27 @@ extension SPStorkPresentationController {
self.gradeView.alpha = self.alpha
})
}
if translation >= self.translateForDismiss {
self.dismissWithConfirmation(prepare: toDefault, completion: {
self.storkDelegate?.didDismissStorkBySwipe?()
})
} else {
toDefault()
}
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)
@@ -293,12 +445,32 @@ extension SPStorkPresentationController {
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 {
@@ -312,9 +484,24 @@ extension SPStorkPresentationController {
self.presentedView?.transform = CGAffineTransform(translationX: 0, y: translationForModal)
let factor = 1 + (translationForModal / 6000)
self.snapshotView?.transform = CGAffineTransform.init(scaleX: factor, y: factor)
self.gradeView.alpha = self.alpha - ((factor - 1) * 15)
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.workConfirmation {
if self.hapticMoments.contains(.willDismissIfRelease) {
self.feedbackGenerator.impactOccurred()
}
}
}
}
}
}
@@ -326,10 +513,7 @@ extension SPStorkPresentationController {
guard let containerView = containerView else { return }
self.updateSnapshotAspectRatio()
if presentedViewController.view.isDescendant(of: containerView) {
UIView.animate(withDuration: 0.1) { [weak self] in
guard let `self` = self else { return }
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
}
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
}
}
@@ -337,6 +521,7 @@ extension SPStorkPresentationController {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { contex in
self.updateLayoutIndicator()
self.updateLayoutCloseButton()
}, completion: { [weak self] _ in
self?.updateSnapshotAspectRatio()
self?.updateSnapshot()
@@ -344,11 +529,15 @@ extension SPStorkPresentationController {
}
private func updateLayoutIndicator() {
guard let presentedView = self.presentedView else { return }
self.indicatorView.style = .line
self.indicatorView.sizeToFit()
self.indicatorView.frame.origin.y = 12
self.indicatorView.center.x = presentedView.frame.width / 2
self.indicatorView.style = .arrow
}
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() {
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -25,15 +25,14 @@ final class SPStorkPresentingAnimationController: NSObject, UIViewControllerAnim
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),
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -23,16 +23,36 @@ import UIKit
public final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
public var isSwipeToDismissEnabled: Bool = true
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 indicatorMode: SPStorkArrowMode = .auto
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 weak var confirmDelegate: SPStorkControllerConfirmDelegate? = nil
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let controller = SPStorkPresentationController(presentedViewController: presented, presenting: presenting)
controller.isSwipeToDismissEnabled = self.isSwipeToDismissEnabled
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.indicatorMode = self.indicatorMode
controller.customHeight = self.customHeight
controller.translateForDismiss = self.translateForDismiss
controller.cornerRadius = self.cornerRadius
controller.hapticMoments = self.hapticMoments
controller.transitioningDelegate = self
controller.storkDelegate = self.storkDelegate
controller.confirmDelegate = self.confirmDelegate
return controller
}
@@ -0,0 +1,78 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
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)
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,26 +21,30 @@
import UIKit
class SPEmptyProposeLabel: UILabel {
open class SPStorkCloseView: UIView {
var color = UIColor.blue {
didSet {
self.setNeedsDisplay()
}
}
init() {
super.init(frame: CGRect.zero)
self.commonInit()
}
init(title: String) {
super.init(frame: CGRect.zero)
self.text = title
self.commonInit()
}
private func commonInit() {
self.setCenteringAlignment()
self.font = UIFont.system(type: .Regular, size: 14)
self.textColor = SPNativeStyleKit.Colors.gray
self.backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override open func draw(_ rect: CGRect) {
super.draw(rect)
SPStorkCodeDraw.drawClose(frame: rect, resizing: .aspectFit, color: self.color)
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,24 +21,49 @@
import UIKit
class SPStorkIndicatorView: UIView {
open class SPStorkIndicatorView: UIView {
var style: Style = .line {
didSet {
switch self.style {
case .line:
self.animate {
self.leftView.transform = .identity
self.rightView.transform = .identity
}
case .arrow:
self.animate {
let angle = CGFloat(20 * Float.pi / 180)
self.leftView.transform = CGAffineTransform.init(rotationAngle: angle)
self.rightView.transform = CGAffineTransform.init(rotationAngle: -angle)
if self.mode == .auto {
switch self.style {
case .line:
self.animate {
self.leftView.transform = .identity
self.rightView.transform = .identity
}
case .arrow:
self.animate {
let angle = CGFloat(20 * Float.pi / 180)
self.leftView.transform = CGAffineTransform.init(rotationAngle: angle)
self.rightView.transform = CGAffineTransform.init(rotationAngle: -angle)
}
}
}
if self.mode == .alwaysArrow {
self.leftView.layer.removeAllAnimations()
self.rightView.layer.removeAllAnimations()
self.leftView.transform = .identity
self.rightView.transform = .identity
let angle = CGFloat(20 * Float.pi / 180)
self.leftView.transform = CGAffineTransform.init(rotationAngle: angle)
self.rightView.transform = CGAffineTransform.init(rotationAngle: -angle)
}
if self.mode == .alwaysLine {
self.leftView.transform = .identity
self.rightView.transform = .identity
}
}
}
var mode: SPStorkArrowMode = .auto
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
}
}
@@ -50,16 +75,19 @@ class SPStorkIndicatorView: UIView {
self.backgroundColor = UIColor.clear
self.addSubview(self.leftView)
self.addSubview(self.rightView)
self.leftView.backgroundColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
self.rightView.backgroundColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
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.leftView.transform = .identity
self.rightView.transform = .identity
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: 36, height: 13)
let height: CGFloat = 5
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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,
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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],
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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)!,
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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,
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
import UIKit
struct SPApp {
enum SPApp {
static var udid: String? {
return UIDevice.current.identifierForVendor?.uuidString
@@ -35,6 +35,14 @@ struct SPApp {
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
@@ -56,5 +64,10 @@ struct SPApp {
}
}
private init() {}
static func set(elementsColor: UIColor) {
UINavigationController.elementsColor = elementsColor
UIAlertController.elementsColor = elementsColor
UITabBarController.elementsColor = elementsColor
UITabBar.appearance().tintColor = elementsColor
}
}
@@ -0,0 +1,44 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
extension SPApp {
struct Badge {
static var number: Int {
get {
return UIApplication.shared.applicationIconBadgeNumber
}
set {
UIApplication.shared.applicationIconBadgeNumber = newValue
}
}
static func reset() {
UIApplication.shared.applicationIconBadgeNumber = 0
}
private init() {}
}
}
@@ -0,0 +1,48 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
extension SPApp {
enum Launch {
static func run() {
self.count += 1
if (UserDefaults.standard.object(forKey: "SPDateFirstLaunch") as? Date) == nil {
UserDefaults.standard.set(Date(), forKey: "SPDateFirstLaunch")
}
}
static var count: Int {
get { return UserDefaults.standard.value(forKey: "SPLaunchCount") as? Int ?? 0 }
set { UserDefaults.standard.set(newValue, forKey: "SPLaunchCount") }
}
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 Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
import 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)})
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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)})
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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?()
}
}
@@ -1,60 +0,0 @@
// 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 LocalAuthentication
struct SPLocalAuthentication {
static var isEnable: Bool {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
return true
} else {
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
return true
} else {
return false
}
}
}
static func request(reason: String, complecton: @escaping (Bool)->()) {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, error in
DispatchQueue.main.async { complecton(success) }
}
} else {
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, error in
DispatchQueue.main.async { complecton(success) }
}
} else {
complecton(false)
}
}
}
private init() {}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,4 +21,4 @@
import UIKit
struct SPCodeDraw { private init(){} }
struct SPCodeDraw { private init() {} }
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,17 +21,21 @@
import UIKit
public struct SPConstraints {
struct SPConstraints {
static func setEqualSize(_ view: UIView, superVuew: UIView) {
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: superVuew.topAnchor),
view.leftAnchor.constraint(equalTo: superVuew.leftAnchor),
view.rightAnchor.constraint(equalTo: superVuew.rightAnchor),
view.bottomAnchor.constraint(equalTo: superVuew.bottomAnchor)
])
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() {}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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()
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -23,11 +23,11 @@ import UIKit
struct SPDevice {
static var isIphone: Bool {
static var iphone: Bool {
return UIDevice.current.userInterfaceIdiom == .phone
}
static var isIpad: Bool {
static var ipad: Bool {
return UIDevice.current.userInterfaceIdiom == .pad
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -43,7 +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
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -23,17 +23,29 @@ import UIKit
extension CGRect {
var bottomXPosition: CGFloat {
var bottomX: CGFloat {
get { return self.origin.x + self.width }
set { self.origin.x = newValue - self.width }
}
var bottomYPosition: CGFloat {
var bottomY: CGFloat {
get { return self.origin.y + self.height }
set { self.origin.y = newValue - self.height }
}
var minSideSize: CGFloat {
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)
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,42 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Foundation
extension Date {
func format(mask: String) -> String {
let dateFormatter = DateFormatter()
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
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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,7 +70,32 @@ extension String {
return false
}
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])
}
}
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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)
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -60,8 +60,11 @@ 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) {
@@ -69,6 +72,11 @@ extension UIButton {
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
}
func setImage(_ image: UIImage) {
self.setImage(image, for: .normal)
self.imageView?.contentMode = .scaleAspectFit
}
func removeAllTargets() {
self.removeTarget(nil, action: nil, for: .allEvents)
}
@@ -98,11 +106,11 @@ extension UIButton {
}
func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
SPAnimation.animate(0.2, animations: {
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()
@@ -111,7 +119,7 @@ extension UIButton {
}
func hideContent(completion: (() -> Void)! = {}) {
SPAnimation.animate(0.2, animations: {
SPAnimation.animate(0.25, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
completion()
@@ -119,7 +127,7 @@ extension UIButton {
}
func showContent(completion: (() -> Void)! = {}) {
SPAnimation.animate(0.2, animations: {
SPAnimation.animate(0.25, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
completion()
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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()
}
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,7 @@
import UIKit
public extension UIColor {
extension UIColor {
convenience init(hex: String) {
var red: CGFloat = 0.0
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,91 +21,41 @@
import UIKit
public extension UIFont {
extension UIFont {
public struct fonts {
public static func AvenirNext(type: BoldType, size: CGFloat) -> UIFont {
return UIFont.createFont(.AvenirNext, boldType: type, size: size)
}
static func system(weight: FontWeight, size: CGFloat) -> UIFont {
return UIFont.systemFont(ofSize: size, weight: self.weight(for: weight))
}
public static func system(type: BoldType, size: CGFloat) -> UIFont {
if #available(iOS 8.2, *) {
return UIFont.systemFont(ofSize: size, weight: self.getBoldTypeBy(boldType: type))
} else {
return self.createFont(.AvenirNext, boldType: type, size: size)
}
}
public static func createFont(_ fontType: FontType, boldType: BoldType, size: CGFloat) -> UIFont {
return UIFont.init(
name: self.getFontNameBy(fontType: fontType) + self.getBoldTypeNameBy(boldType: boldType),
size: size
)!
}
private static func getFontNameBy(fontType: FontType) -> String {
switch fontType {
case .AvenirNext:
return "AvenirNext"
}
}
private static func getBoldTypeNameBy(boldType: BoldType) -> String {
switch boldType {
case .UltraLight:
return "-UltraLight"
case .Light:
return "-Light"
case .Medium:
return "-Medium"
case .Regular:
return "-Regular"
case .Bold:
return "-Bold"
case .DemiBold:
return "-DemiBold"
default:
return "-Regular"
}
}
@available(iOS 8.2, *)
private static func getBoldTypeBy(boldType: BoldType) -> UIFont.Weight {
switch boldType {
case .UltraLight:
private static func weight(for weight: FontWeight) -> UIFont.Weight {
switch weight {
case .ultraLight:
return UIFont.Weight.ultraLight
case .Light:
case .light:
return UIFont.Weight.light
case .Medium:
case .medium:
return UIFont.Weight.medium
case .Regular:
case .regular:
return UIFont.Weight.regular
case .Bold:
case .bold:
return UIFont.Weight.bold
case .DemiBold:
case .demiBold:
return UIFont.Weight.semibold
case .Heavy:
case .heavy:
return UIFont.Weight.heavy
default:
return UIFont.Weight.regular
}
}
public enum FontType {
case AvenirNext
}
public enum BoldType {
case Regular
case Medium
case Light
case UltraLight
case Heavy
case Bold
case DemiBold
case None
enum FontWeight {
case regular
case medium
case light
case ultraLight
case heavy
case bold
case demiBold
case none
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,9 +21,9 @@
import UIKit
public extension UIImage {
extension UIImage {
public func resize(width: CGFloat) -> UIImage {
func resize(width: CGFloat) -> UIImage {
let scale = width / self.size.width
let newHeight = self.size.height * scale
UIGraphicsBeginImageContext(CGSize(width: width, height: newHeight))
@@ -32,4 +32,12 @@ public extension UIImage {
UIGraphicsEndImageContext()
return newImage!
}
var alwaysOriginal: UIImage {
return self.withRenderingMode(.alwaysOriginal)
}
var alwaysTemplate: UIImage {
return self.withRenderingMode(.alwaysTemplate)
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -23,13 +23,13 @@ import UIKit
extension UIImageView {
public func setNative() {
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 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
}
@@ -47,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)
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -28,7 +28,7 @@ extension UINavigationController {
if UINavigationBar.appearance().tintColor != nil {
return UINavigationBar.appearance().tintColor
} else {
return SPNativeStyleKit.Colors.blue
return SPNativeColors.blue
}
}
set {
@@ -21,20 +21,9 @@
import UIKit
public struct SPBadge {
extension UIScrollView {
static func reset() {
UIApplication.shared.applicationIconBadgeNumber = 0
func stopScrolling() {
self.setContentOffset(self.contentOffset, animated: false)
}
static var number: Int {
get {
return UIApplication.shared.applicationIconBadgeNumber
}
set {
UIApplication.shared.applicationIconBadgeNumber = newValue
}
}
private init() {}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -23,6 +23,19 @@ 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(
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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)
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -135,6 +135,10 @@ extension UIViewController {
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
case .noContent:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
}
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -21,7 +21,22 @@
import UIKit
public extension UIView {
extension UIView {
var isDarkMode: Bool {
if #available(iOS 12.0, *) {
if self.traitCollection.userInterfaceStyle == .dark {
return true
} else {
return false
}
} else {
return false
}
}
}
extension UIView {
var viewController: UIViewController? {
get {
@@ -32,37 +47,21 @@ public extension UIView {
}
}
public extension UIView {
extension UIView {
var topSafeArea: CGFloat {
var topSafeArea: CGFloat = 0
var safeArea: UIEdgeInsets {
if #available(iOS 11.0, *) {
topSafeArea = self.safeAreaInsets.top
return self.safeAreaInsets
} else{
return UIEdgeInsets.zero
}
return topSafeArea
}
var bottomSafeArea: CGFloat {
var bottomSafeArea: CGFloat = 0
if #available(iOS 11.0, *) {
bottomSafeArea = self.safeAreaInsets.bottom
}
return bottomSafeArea
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 setHeight(_ height: CGFloat) {
self.frame = CGRect.init(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.width, height: height)
}
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!) }
@@ -78,14 +77,14 @@ public extension UIView {
}
}
func setEqualsBoundsFromSuperview(customWidth: CGFloat? = nil, customHeight: CGFloat? = nil) {
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!))
}
}
@@ -111,23 +110,23 @@ public extension UIView {
)
}
func setYCenteringFromSuperview() {
func setYCenter() {
self.center.y = (self.superview?.frame.height ?? 0) / 2
}
func setXCenteringFromSuperview() {
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)
}
@@ -147,13 +146,13 @@ public extension UIView {
}
}
public extension UIView {
extension UIView {
func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
let gradeView = UIView.init()
gradeView.alpha = 0
self.addSubview(gradeView)
SPConstraints.setEqualSize(gradeView, superVuew: self)
SPConstraints.setEqualSizeSuperview(for: gradeView)
gradeView.alpha = alpha
gradeView.backgroundColor = color
return gradeView
@@ -177,14 +176,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
@@ -280,6 +279,6 @@ extension UIView {
}
func round() {
self.layer.cornerRadius = self.frame.minSideSize / 2
self.layer.cornerRadius = self.frame.minSide / 2
}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -23,20 +23,30 @@ import UIKit
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)
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -20,23 +20,22 @@
// SOFTWARE.
import UIKit
import LocalAuthentication
class SPImageCollectionViewCell: SPCollectionContainerCell<SPDownloadingImageView> {
struct SPLocalAuthentication {
override init(frame: CGRect) {
super.init(frame: frame)
self.view.layer.cornerRadius = 10
self.view.contentMode = .scaleAspectFill
self.view.setNative()
static var isEnable: Bool {
let context = LAContext()
var error: NSError?
return context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
static func request(reason: String, complecton: @escaping (Bool)->()) {
let context = LAContext()
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, error in
DispatchQueue.main.async { complecton(success) }
}
}
override func prepareForReuse() {
super.prepareForReuse()
self.view.contentMode = .scaleAspectFill
self.view.startLoading()
}
private init() {}
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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 Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
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")
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -24,22 +24,26 @@ import UserNotifications
struct SPLocalNotification {
static func add(from timeInterval: TimeInterval, body: String, title: String? = nil, identifier: String? = nil) {
let content = UNMutableNotificationContent()
content.body = body
content.title = title ?? ""
content.badge = NSNumber(value: SPBadge.number + 1)
content.sound = UNNotificationSound.default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false)
let identifier = identifier ?? "\(timeInterval)\(body)\(Int.random(min: 0, max: 1000))"
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: identifier,
content: content,
trigger: trigger
identifier: identificator,
content: self.content,
trigger: self.trigger
)
let center = UNUserNotificationCenter.current()
@@ -50,37 +54,36 @@ struct SPLocalNotification {
}
}
static func add(in date: Date, body: String, title: String? = nil, identifier: String? = nil) {
private var content: UNMutableNotificationContent {
let content = UNMutableNotificationContent()
content.body = body
content.title = title ?? ""
content.badge = NSNumber(value: SPBadge.number + 1)
content.sound = UNNotificationSound.default
content.body = self.text
content.title = self.title ?? ""
content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + self.badge)
content.sound = self.soundEnabled ? UNNotificationSound.default : nil
let triggerDate = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second,], from: date)
let trigger = UNCalendarNotificationTrigger.init(dateMatching: triggerDate, repeats: false)
let identifier = identifier ?? "\(date)\(body)\(Int.random(min: 0, max: 1000))"
let notification = UNNotificationRequest(
identifier: identifier,
content: content,
trigger: trigger
)
let center = UNUserNotificationCenter.current()
center.add(notification) { (error) in
if let error = error {
print("SPLocalNotification - \(error)")
if let category = self.category {
if #available(iOS 12.0, *) {
let notificationCategory = UNNotificationCategory(identifier: category.identifier, actions: category.actions, intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: nil, categorySummaryFormat: category.summary, options: [])
UNUserNotificationCenter.current().setNotificationCategories([notificationCategory])
content.categoryIdentifier = notificationCategory.identifier
}
}
return content
}
static func remove(identifier: String) {
let center = UNUserNotificationCenter.current()
center.removePendingNotificationRequests(withIdentifiers: [identifier])
private var trigger: UNTimeIntervalNotificationTrigger {
return UNTimeIntervalNotificationTrigger(timeInterval: self.timeInterval, repeats: false)
}
}
struct SPLocalNotificationCategory {
var identifier: String
var summary: String
var actions: [UNNotificationAction] = []
static var countSymbol: String {
return "%u"
}
private init() {}
}
@@ -1,111 +0,0 @@
// 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
struct SPOpener {
struct App {
static func system(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("SPOpener - Photos opened: \(success)")
})
} else {
UIApplication.shared.openURL(settingsUrl as URL)
}
} else {
print("SPOpener - 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("SPOpener - Settings opened: \(success)")
})
} else {
UIApplication.shared.openURL(settingsUrl as URL)
}
} else {
print("SPOpener - Settings not opened")
}
}
}
}
private init() {}
}
struct Link {
public static func redirectToBrowserAndOpen(link: String) {
guard let url = URL(string: link) else {
print("SPOpener - can not create URL")
return
}
self.redirectToBrowserAndOpen(link: url)
}
public static func redirectToBrowserAndOpen(link: URL) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(link, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
} else {
UIApplication.shared.openURL(link)
}
}
public static func openInsideApp(link: String, on viewController: UIViewController) {
if let url = URL.init(string: link) {
let safariController = SFSafariViewController.init(url: url)
viewController.present(safariController, animated: true, completion: nil)
} else {
print("SPOpener - openInsideApp - invalid link")
}
}
private init() {}
}
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)})
}
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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,52 +34,47 @@ public extension String {
}
}
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)
}
}
@@ -89,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 {
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
// Copyright © 2017 Ivan Varabei (varabeis@icloud.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -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)

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