Compare commits

...

417 Commits

Author SHA1 Message Date
Ivan Vorobei 7612456ea9 Merge pull request #120 from izyumkin/master
Typo fixed
2022-12-25 21:41:57 +03:00
izzyumkin c44ab84eca Typo fixed 2022-12-25 17:55:44 +05:00
Ivan Vorobei f08daeac2b Update README.md 2021-10-01 14:21:36 +03:00
Ivan Vorobei 9ac9e4374d Update README.md 2021-07-13 16:50:53 +03:00
Ivan Vorobei 3be7590fa2 Merge pull request #118 from elilien/master
Made the demo project support iPad and silenced two warnings with Xcode 12.5 and Swift 5.4
2021-05-28 22:19:56 +03:00
Kaixin L 801a6c8603 Made the demo project support iPad and fixed an issue that would cause the demo app to crash 2021-05-29 03:10:10 +08:00
Kaixin L e397952e6f Silenced two warnings with Xcode 12.5 and Swift 5.4 2021-05-29 03:10:09 +08:00
Ivan Vorobei 314e83dd26 Updated Readme. 2021-05-22 10:31:18 +03:00
Ivan Vorobei 965667b373 Update Readme. 2021-04-21 10:43:44 +03:00
Ivan Vorobei 9eea2c3259 Fix links. 2021-03-25 21:49:10 +03:00
Ivan Vorobei 7e58625b8e Fix readme 2021-02-14 11:52:28 +03:00
Ivan Vorobei 70a0a261cc Update version 2020-12-20 01:02:07 +03:00
Ivan Vorobei 214fe1cffd Update 2020-12-20 00:59:54 +03:00
Ivan Vorobei 5222fc5a13 Update README.md 2020-10-02 10:11:25 +03:00
Ivan Vorobei b4bf86c56b Update README.md 2020-07-02 22:24:16 +03:00
Ivan Vorobei e0ba55a4a8 Update README.md 2020-07-02 22:23:47 +03:00
Ivan Vorobei 5696d657db Update README.md 2020-07-02 22:22:08 +03:00
Ivan Vorobei b698f106af Update README.md 2020-07-02 22:21:46 +03:00
Ivan Vorobei ccd0be4f01 Update README.md 2020-07-02 22:21:24 +03:00
Ivan Vorobei 682208fa21 Update readme 2020-07-02 22:21:04 +03:00
Ivan Vorobei c7d9c7730a Remove xcode-shop link 2020-07-02 22:18:10 +03:00
Ivan Vorobei dd43a80271 Update sponsors and readme 2020-07-02 22:17:36 +03:00
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
Ivan Vorobei 1500c272b1 Update to 1.1.8 version
- Fix bugs with paramtrs
- Add paramtr custom height
- Update Readme
2018-12-12 16:04:25 +03:00
Ivan Vorobei ba859e3646 Update README.md 2018-12-12 12:47:52 +03:00
Ivan Vorobei 01b8702c4a Update README.md 2018-12-12 12:46:22 +03:00
Ivan Vorobei ec809ee1c5 Update Readme.md 2018-12-12 12:28:53 +03:00
Ivan Vorobei a5f41ab3d6 Update gif 2018-12-12 12:27:47 +03:00
Ivan Vorobei 58554f1449 Update README.md 2018-12-12 11:43:18 +03:00
Ivan Vorobei 8c1b6e2c8d Add custom height 2018-12-10 21:39:48 +03:00
Ivan Vorobei 724635e387 Update README.md 2018-12-10 21:36:35 +03:00
Ivan Vorobei dfc0fb8e52 Update gif 2018-12-10 21:34:47 +03:00
Ivan Vorobei 50c9ab4104 Create gif-mockup.gif 2018-12-10 21:33:23 +03:00
Ivan Vorobei bb0821d2cc Update README.md 2018-12-07 13:45:03 +03:00
Ivan Vorobei ae29eba5b9 Update README.md 2018-12-07 13:44:30 +03:00
Ivan Vorobei 0c98c3c699 Update to 1.1.6 2018-12-07 13:40:49 +03:00
Ivan Vorobei 4ecc8e96d8 Update Readme.md 2018-12-07 13:40:13 +03:00
Ivan Vorobei fac454f41d remove source 2018-12-07 13:14:31 +03:00
Ivan Vorobei ae68a1be5d Update README.md 2018-12-06 17:33:44 +03:00
Ivan Vorobei c111a20c86 Update README.md 2018-12-06 17:28:05 +03:00
Ivan Vorobei cc1b1ac604 Update SPStorkPresentationController.swift 2018-12-06 17:14:37 +03:00
Ivan Vorobei 265f77dd5e Update README.md 2018-12-06 15:35:02 +03:00
Ivan Vorobei aacf153d0e Update README.md 2018-12-06 15:26:51 +03:00
Ivan Vorobei 5b8fdf6590 Update to 1.1.1 2018-12-06 15:21:50 +03:00
Ivan Vorobei 883ade3052 Update README.md 2018-12-06 15:08:41 +03:00
Ivan Vorobei 44837098ea Update to version 1.1 2018-12-06 15:05:47 +03:00
Ivan Vorobei 42063749ed Update README.md 2018-12-04 12:20:52 +03:00
Ivan Vorobei 464df2eb5c Update README.md 2018-12-04 12:19:31 +03:00
Ivan Vorobei 6f48df5761 Update README.md 2018-12-04 12:10:54 +03:00
Ivan Vorobei e331793afc Update README.md 2018-12-04 12:10:23 +03:00
Ivan Vorobei 82152b698a Update README.md 2018-12-03 14:33:11 +03:00
Ivan Vorobei 75eaaec598 Update Readme 2018-12-03 10:53:25 +03:00
Ivan Vorobei d2cc2e424c Update README.md 2018-12-02 21:13:33 +03:00
Ivan Vorobei 0ac35b4494 Update README.md 2018-12-02 21:13:18 +03:00
Ivan Vorobei 6c2600dea8 Update README.md 2018-12-01 23:36:18 +03:00
Ivan Vorobei 26d7422216 Update README.md 2018-12-01 23:35:01 +03:00
Ivan Vorobei a4a02b598f Update README.md 2018-11-30 20:54:40 +03:00
Ivan Vorobei 0309bb2e0e Update example and Readme 2018-11-30 20:43:35 +03:00
Ivan Vorobei 4e0507f132 Update README.md 2018-11-30 20:33:04 +03:00
Ivan Vorobei 8e33ff7614 Add donate banner 2018-11-30 20:32:36 +03:00
Ivan Vorobei b7ab9e0327 Update README.md 2018-11-30 20:30:39 +03:00
Ivan Vorobei 4129f23d00 Add header banner 2018-11-30 20:29:57 +03:00
Ivan Vorobei 91b01d83d9 Update Readme 2018-11-30 20:26:33 +03:00
Ivan Vorobei 4eda691bd0 Update README.md 2018-11-30 20:25:06 +03:00
Ivan Vorobei 0889352299 Update Readme 2018-11-30 20:18:38 +03:00
Ivan Vorobei 34f267d43c Update SPStorkController.podspec 2018-11-29 12:55:29 +03:00
Ivan Vorobei 425f8ec1ca Fix layout for SPStorkController
- Fix layout for SPStorkController
- Add extenshion for `CGRect` static var `displayFrame`. It is rect for screen size
- Set clear background color for `SPStorkIndicatorView`
2018-11-29 12:49:56 +03:00
Ivan Vorobei 25ea7eab4c Add more interactive to SPStorkController 2018-11-29 11:10:18 +03:00
Ivan Vorobei 16f060820f Update README.md 2018-11-28 20:11:41 +03:00
Ivan Vorobei 4c8faf7695 Update README.md 2018-11-28 17:49:37 +03:00
Ivan Vorobei 5b843582d4 Update README.md 2018-11-28 17:30:29 +03:00
Ivan Vorobei 8c58dbd2a3 Add example 2018-11-28 17:16:44 +03:00
Ivan Vorobei 366f6dffd1 Update Pospec 2018-11-28 17:16:12 +03:00
Ivan Vorobei 0141560c98 Create Example 2018-11-28 17:14:05 +03:00
Ivan Vorobei 900d4cbbc9 Add example 2018-11-28 17:13:23 +03:00
Ivan Vorobei dd8137b640 Update README.md 2018-11-28 17:07:21 +03:00
Ivan Vorobei 0118f80729 Update readme 2018-11-28 17:06:20 +03:00
199 changed files with 25462 additions and 54 deletions
+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
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:stork-controller.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>stork-controller.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>
@@ -0,0 +1,25 @@
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let navigationController = UINavigationController(rootViewController: Controller())
self.launch(rootViewController: navigationController)
return true
}
func launch(rootViewController: UIViewController) {
let frame = UIScreen.main.bounds
self.window = UIWindow(frame: frame)
self.window!.rootViewController = rootViewController
self.window!.makeKeyAndVisible()
}
}
@@ -0,0 +1,116 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon_20pt@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon_20pt@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon_29pt@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon_29pt@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon_40pt@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon_40pt@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon_60pt@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon_60pt@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon_20pt.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon_20pt@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon_29pt.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon_29pt@2x-1.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon_40pt.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon_40pt@2x-1.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon_76pt.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon_76pt@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "icon_83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
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

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<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="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<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="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</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
}
@@ -0,0 +1,250 @@
// 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 SPFakeBarView: UIView {
public var style: SPFakeBarNavigationStyle = .small {
didSet {
self.updateStyle()
}
}
private var settedHeight: CGFloat = 0
public var height: CGFloat {
get {
return (self.settedHeight) + (self.addStatusBarHeight ? UIApplication.shared.statusBarFrame.height : 0)
}
set {
self.settedHeight = newValue
self.updateHeight()
}
}
public var addStatusBarHeight: Bool = true {
didSet {
self.updateHeight()
}
}
public var elementsColor: UIColor = SPFakeBarView.navigationElementsColor {
didSet {
self.leftButton.setTitleColor(self.elementsColor, for: .normal)
self.leftButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
self.rightButton.setTitleColor(self.elementsColor, for: .normal)
self.rightButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
}
}
public var closeButtonPossition: CloseButtonPosition = .none {
didSet {
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .regular)
switch self.closeButtonPossition {
case .left:
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
case .right:
self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
case .none:
break
}
}
}
public var titleLabel = UILabel.init()
public var subtitleLabel = UILabel.init()
public var leftButton = UIButton.init()
public var rightButton = UIButton.init()
public let separatorView = UIView()
public let blurView: UIVisualEffectView = {
let effect = UIBlurEffect(style: .extraLight)
return UIVisualEffectView.init(effect: effect)
}()
private var titleBottomConstraint: NSLayoutConstraint?
private var heightConstraint: NSLayoutConstraint?
private var topConstraint: NSLayoutConstraint?
private var leadingConstraint: NSLayoutConstraint?
private var trailingConstraint: NSLayoutConstraint?
public init(style: SPFakeBarNavigationStyle) {
super.init(frame: CGRect.zero)
self.style = style
self.commonInit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.style = .small
self.commonInit()
}
private func commonInit() {
self.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.blurView)
self.blurView.translatesAutoresizingMaskIntoConstraints = false
self.blurView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
self.blurView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.blurView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
self.blurView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
self.addSubview(self.separatorView)
self.separatorView.backgroundColor = UIColor.init(red: 191 / 255.0, green: 191 / 255.0, blue: 191 / 255.0, alpha: 1)
self.separatorView.translatesAutoresizingMaskIntoConstraints = false
self.separatorView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
self.separatorView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.separatorView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
self.separatorView.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
self.addSubview(self.titleLabel)
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
self.titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
self.titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16).isActive = true
self.titleBottomConstraint = self.titleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12)
self.titleBottomConstraint?.isActive = true
self.addSubview(self.subtitleLabel)
self.subtitleLabel.textColor = UIColor.init(red: 142 / 255.0, green: 142 / 255.0, blue: 146 / 255.0, alpha: 1)
self.subtitleLabel.font = UIFont.systemFont(ofSize: 13, weight: .semibold)
self.subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
self.subtitleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16).isActive = true
self.subtitleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16).isActive = true
self.subtitleLabel.bottomAnchor.constraint(equalTo: self.titleLabel.topAnchor, constant: 0).isActive = true
self.leftButton.setTitleColor(self.elementsColor, for: .normal)
self.leftButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
self.leftButton.titleLabel?.textAlignment = .left
self.leftButton.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
self.leftButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 17, bottom: 0, right: 0)
self.addSubview(self.leftButton)
self.leftButton.translatesAutoresizingMaskIntoConstraints = false
self.leftButton.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
self.leftButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12).isActive = true
self.rightButton.setTitleColor(self.elementsColor, for: .normal)
self.rightButton.setTitleColor(self.elementsColor.withAlphaComponent(0.7), for: .highlighted)
self.rightButton.titleLabel?.textAlignment = .right
self.rightButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
self.rightButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 16)
self.addSubview(self.rightButton)
self.rightButton.translatesAutoresizingMaskIntoConstraints = false
self.rightButton.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
self.rightButton.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -12).isActive = true
self.closeButtonPossition = .none
self.setContraints()
self.updateStyle()
}
override open func layoutSubviews() {
super.layoutSubviews()
self.setContraints()
}
private func setContraints() {
if let superview = self.superview {
if self.topConstraint == nil {
self.topConstraint = self.topAnchor.constraint(equalTo: superview.topAnchor)
self.topConstraint?.isActive = true
self.leadingConstraint = self.leadingAnchor.constraint(equalTo: superview.leadingAnchor)
self.leadingConstraint?.isActive = true
self.trailingConstraint = self.trailingAnchor.constraint(equalTo: superview.trailingAnchor)
self.trailingConstraint?.isActive = true
self.heightConstraint = self.heightAnchor.constraint(equalToConstant: self.height)
self.heightConstraint?.isActive = true
self.updateHeight()
}
}
}
private func updateStyle() {
switch self.style {
case .small:
if UIApplication.shared.statusBarFrame.height == 44 {
self.height = 88 - 44
self.titleBottomConstraint?.constant = -12
} else {
self.height = 64 - 20
self.titleBottomConstraint?.constant = -12
}
self.addStatusBarHeight = true
self.titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
self.titleLabel.textAlignment = .center
case .stork:
self.height = 66
self.titleBottomConstraint?.constant = -12
self.addStatusBarHeight = false
self.titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
self.titleLabel.textAlignment = .center
case .large:
if UIApplication.shared.statusBarFrame.height == 44 {
self.height = 140 - 44
self.titleBottomConstraint?.constant = -8
} else {
self.height = 116 - 20
self.titleBottomConstraint?.constant = -4
}
self.addStatusBarHeight = true
self.titleLabel.font = UIFont.systemFont(ofSize: 34, weight: .bold)
self.titleLabel.textAlignment = .left
break
case .noContent:
self.height = 0
self.addStatusBarHeight = true
}
self.updateConstraints()
}
private func updateHeight() {
self.heightConstraint?.constant = self.height
self.updateConstraints()
}
public enum CloseButtonPosition {
case left
case right
case none
}
}
extension SPFakeBarView {
static var navigationElementsColor: UIColor {
get {
if UINavigationBar.appearance().tintColor != nil {
return UINavigationBar.appearance().tintColor
} else {
return UIColor.init(red: 0 / 255.0, green: 122 / 255.0, blue: 255 / 255.0, alpha: 1)
}
}
set {
UINavigationBar.appearance().tintColor = newValue
}
}
}
@@ -0,0 +1,88 @@
import UIKit
class SPStorkCodeDraw : NSObject {
private struct Cache {
static let gradient: CGGradient = CGGradient(colorsSpace: nil, colors: [UIColor.red.cgColor, UIColor.red.cgColor] as CFArray, locations: [0, 1])!
}
@objc dynamic class var gradient: CGGradient { return Cache.gradient }
@objc dynamic class func drawClose(frame targetFrame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100), resizing: ResizingBehavior = .aspectFit, color: UIColor = UIColor(red: 0.000, green: 0.000, blue: 0.000, alpha: 1.000)) {
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 100, height: 100), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 100, y: resizedFrame.height / 100)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 92.02, y: 22.92))
bezierPath.addLine(to: CGPoint(x: 64.42, y: 50.52))
bezierPath.addLine(to: CGPoint(x: 92.02, y: 78.13))
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 92.99), controlPoint1: CGPoint(x: 96.13, y: 82.23), controlPoint2: CGPoint(x: 96.13, y: 88.89))
bezierPath.addCurve(to: CGPoint(x: 84.59, y: 96.07), controlPoint1: CGPoint(x: 89.97, y: 95.05), controlPoint2: CGPoint(x: 87.28, y: 96.07))
bezierPath.addCurve(to: CGPoint(x: 77.16, y: 92.99), controlPoint1: CGPoint(x: 81.9, y: 96.07), controlPoint2: CGPoint(x: 79.22, y: 95.05))
bezierPath.addLine(to: CGPoint(x: 49.55, y: 65.38))
bezierPath.addLine(to: CGPoint(x: 21.95, y: 92.99))
bezierPath.addCurve(to: CGPoint(x: 14.51, y: 96.07), controlPoint1: CGPoint(x: 19.89, y: 95.05), controlPoint2: CGPoint(x: 17.2, y: 96.07))
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 92.99), controlPoint1: CGPoint(x: 11.82, y: 96.07), controlPoint2: CGPoint(x: 9.13, y: 95.05))
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 78.13), controlPoint1: CGPoint(x: 2.97, y: 88.89), controlPoint2: CGPoint(x: 2.97, y: 82.23))
bezierPath.addLine(to: CGPoint(x: 34.69, y: 50.52))
bezierPath.addLine(to: CGPoint(x: 7.08, y: 22.92))
bezierPath.addCurve(to: CGPoint(x: 7.08, y: 8.04), controlPoint1: CGPoint(x: 2.97, y: 18.8), controlPoint2: CGPoint(x: 2.97, y: 12.15))
bezierPath.addCurve(to: CGPoint(x: 21.94, y: 8.04), controlPoint1: CGPoint(x: 11.18, y: 3.94), controlPoint2: CGPoint(x: 17.84, y: 3.94))
bezierPath.addLine(to: CGPoint(x: 49.55, y: 35.65))
bezierPath.addLine(to: CGPoint(x: 77.16, y: 8.04))
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 8.04), controlPoint1: CGPoint(x: 81.26, y: 3.94), controlPoint2: CGPoint(x: 87.92, y: 3.94))
bezierPath.addCurve(to: CGPoint(x: 92.02, y: 22.92), controlPoint1: CGPoint(x: 96.13, y: 12.15), controlPoint2: CGPoint(x: 96.13, y: 18.8))
bezierPath.close()
color.setFill()
bezierPath.fill()
context.restoreGState()
}
@objc(StyleKitNameResizingBehavior)
enum ResizingBehavior: Int {
case aspectFit
case aspectFill
case stretch
case center
func apply(rect: CGRect, target: CGRect) -> CGRect {
if rect == target || target == CGRect.zero {
return rect
}
var scales = CGSize.zero
scales.width = abs(target.width / rect.width)
scales.height = abs(target.height / rect.height)
switch self {
case .aspectFit:
scales.width = min(scales.width, scales.height)
scales.height = scales.width
case .aspectFill:
scales.width = max(scales.width, scales.height)
scales.height = scales.width
case .stretch:
break
case .center:
scales.width = 1
scales.height = 1
}
var result = rect.standardized
result.size.width *= scales.width
result.size.height *= scales.height
result.origin.x = target.minX + (target.width - result.width) / 2
result.origin.y = target.minY + (target.height - result.height) / 2
return result
}
}
private override init() {}
}
@@ -0,0 +1,51 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan 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: AnyObject {
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: AnyObject {
@objc optional func didDismissStorkBySwipe()
@objc optional func didDismissStorkByTap()
}
@@ -0,0 +1,105 @@
// 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 SPStorkController {
static public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let controller = self.controller(for: scrollView) {
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 * 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) {
nextResponder = nextResponder!.next
}
return nextResponder as? UIViewController
}
}
@@ -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()
}
}
@@ -0,0 +1,54 @@
// 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
final class SPStorkDismissingAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let presentedViewController = transitionContext.viewController(forKey: .from) else {
return
}
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
let containerView = transitionContext.containerView
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: finalFrameForPresentedView.width, height: finalFrameForPresentedView.height)
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: .curveEaseIn,
animations: {
presentedViewController.view.frame = offscreenFrame
}) { finished in
transitionContext.completeTransition(finished)
}
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6
}
}
@@ -0,0 +1,599 @@
// 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
class SPStorkPresentationController: UIPresentationController, UIGestureRecognizerDelegate {
var swipeToDismissEnabled: Bool = true
var tapAroundToDismissEnabled: Bool = true
var showCloseButton: Bool = false
var showIndicator: Bool = true
var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
var hideIndicatorWhenScroll: Bool = false
var indicatorMode: SPStorkArrowMode = .auto
var customHeight: CGFloat? = nil
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()
private var snapshotView: UIView?
private let backgroundView = UIView()
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 let alpha: CGFloat = 0.51
var cornerRadius: CGFloat = 10
private var scaleForPresentingView: CGFloat {
guard let containerView = containerView else { return 0 }
let factor = 1 - ((self.cornerRadius + 3) * 2 / containerView.frame.width)
return factor
}
private var feedbackGenerator = UIImpactFeedbackGenerator(style: .light)
override var presentedView: UIView? {
let view = self.presentedViewController.view
if view?.frame.origin == CGPoint.zero {
view?.frame = self.frameOfPresentedViewInContainerView
}
return view
}
override var frameOfPresentedViewInContainerView: CGRect {
guard let containerView = containerView else { return .zero }
let baseY: CGFloat = self.topSpace + 13
let maxHeight: CGFloat = containerView.bounds.height - baseY
var height: CGFloat = maxHeight
if let customHeight = self.customHeight {
if customHeight < maxHeight {
height = customHeight
} else {
print("SPStorkController - Custom height change to default value. Your height more maximum value")
}
}
return CGRect(x: 0, y: containerView.bounds.height - height, width: containerView.bounds.width, height: height)
}
override func presentationTransitionWillBegin() {
super.presentationTransitionWillBegin()
if !self.hapticMoments.isEmpty {
self.feedbackGenerator.prepare()
}
guard let containerView = self.containerView, let presentedView = self.presentedView, let window = containerView.window else { return }
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
containerView.insertSubview(self.snapshotViewContainer, belowSubview: presentedViewController.view)
self.snapshotViewContainer.frame = initialFrame
self.updateSnapshot()
self.snapshotView?.layer.cornerRadius = 0
self.backgroundView.backgroundColor = UIColor.black
self.backgroundView.translatesAutoresizingMaskIntoConstraints = false
containerView.insertSubview(self.backgroundView, belowSubview: self.snapshotViewContainer)
NSLayoutConstraint.activate([
self.backgroundView.topAnchor.constraint(equalTo: window.topAnchor),
self.backgroundView.leftAnchor.constraint(equalTo: window.leftAnchor),
self.backgroundView.rightAnchor.constraint(equalTo: window.rightAnchor),
self.backgroundView.bottomAnchor.constraint(equalTo: window.bottomAnchor)
])
let transformForSnapshotView = CGAffineTransform.identity
.translatedBy(x: 0, y: -snapshotViewContainer.frame.origin.y)
.translatedBy(x: 0, y: self.topSpace)
.translatedBy(x: 0, y: -snapshotViewContainer.frame.height / 2)
.scaledBy(x: scaleForPresentingView, y: scaleForPresentingView)
.translatedBy(x: 0, y: snapshotViewContainer.frame.height / 2)
self.addCornerRadiusAnimation(for: self.snapshotView, cornerRadius: self.cornerRadius, duration: 0.6)
self.snapshotView?.layer.masksToBounds = true
if #available(iOS 11.0, *) {
presentedView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
}
presentedView.layer.cornerRadius = self.cornerRadius
presentedView.layer.masksToBounds = true
var rootSnapshotView: UIView?
var rootSnapshotRoundedView: UIView?
if presentingViewController.isPresentedAsStork {
guard let rootController = presentingViewController.presentingViewController, let snapshotView = rootController.view.snapshotView(afterScreenUpdates: false) else { return }
containerView.insertSubview(snapshotView, aboveSubview: self.backgroundView)
snapshotView.frame = initialFrame
snapshotView.transform = transformForSnapshotView
snapshotView.alpha = 1 - self.alpha
snapshotView.layer.cornerRadius = self.cornerRadius
snapshotView.contentMode = .top
snapshotView.layer.masksToBounds = true
rootSnapshotView = snapshotView
let snapshotRoundedView = UIView()
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
snapshotRoundedView.layer.masksToBounds = true
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
snapshotRoundedView.frame = initialFrame
snapshotRoundedView.transform = transformForSnapshotView
rootSnapshotRoundedView = snapshotRoundedView
}
presentedViewController.transitionCoordinator?.animate(
alongsideTransition: { [weak self] context in
guard let `self` = self else { return }
self.snapshotView?.transform = transformForSnapshotView
self.gradeView.alpha = self.alpha
}, completion: { _ in
self.snapshotView?.transform = .identity
rootSnapshotView?.removeFromSuperview()
rootSnapshotRoundedView?.removeFromSuperview()
})
if self.hapticMoments.contains(.willPresent) {
self.feedbackGenerator.impactOccurred()
}
}
override func presentationTransitionDidEnd(_ completed: Bool) {
super.presentationTransitionDidEnd(completed)
guard let containerView = containerView else { return }
self.updateSnapshot()
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
self.snapshotViewContainer.transform = .identity
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = false
self.snapshotViewContainer.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
self.updateSnapshotAspectRatio()
if self.tapAroundToDismissEnabled {
self.tap = UITapGestureRecognizer.init(target: self, action: #selector(self.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
self.presentedViewController.view.addGestureRecognizer(self.pan!)
}
}
override func dismissalTransitionWillBegin() {
super.dismissalTransitionWillBegin()
guard let containerView = containerView else { return }
self.startDismissing = true
let initialFrame: CGRect = presentingViewController.isPresentedAsStork ? presentingViewController.view.frame : containerView.bounds
let initialTransform = CGAffineTransform.identity
.translatedBy(x: 0, y: -initialFrame.origin.y)
.translatedBy(x: 0, y: self.topSpace)
.translatedBy(x: 0, y: -initialFrame.height / 2)
.scaledBy(x: scaleForPresentingView, y: scaleForPresentingView)
.translatedBy(x: 0, y: initialFrame.height / 2)
self.snapshotViewTopConstraint?.isActive = false
self.snapshotViewWidthConstraint?.isActive = false
self.snapshotViewAspectRatioConstraint?.isActive = false
self.snapshotViewContainer.translatesAutoresizingMaskIntoConstraints = true
self.snapshotViewContainer.frame = initialFrame
self.snapshotViewContainer.transform = initialTransform
let finalCornerRadius = presentingViewController.isPresentedAsStork ? self.cornerRadius : 0
let finalTransform: CGAffineTransform = .identity
self.addCornerRadiusAnimation(for: self.snapshotView, cornerRadius: finalCornerRadius, duration: 0.6)
var rootSnapshotView: UIView?
var rootSnapshotRoundedView: UIView?
if presentingViewController.isPresentedAsStork {
guard let rootController = presentingViewController.presentingViewController, let snapshotView = rootController.view.snapshotView(afterScreenUpdates: false) else { return }
containerView.insertSubview(snapshotView, aboveSubview: backgroundView)
snapshotView.frame = initialFrame
snapshotView.transform = initialTransform
snapshotView.contentMode = .top
rootSnapshotView = snapshotView
snapshotView.layer.cornerRadius = self.cornerRadius
snapshotView.layer.masksToBounds = true
let snapshotRoundedView = UIView()
snapshotRoundedView.layer.cornerRadius = self.cornerRadius
snapshotRoundedView.layer.masksToBounds = true
snapshotRoundedView.backgroundColor = UIColor.black.withAlphaComponent(self.alpha)
containerView.insertSubview(snapshotRoundedView, aboveSubview: snapshotView)
snapshotRoundedView.frame = initialFrame
snapshotRoundedView.transform = initialTransform
rootSnapshotRoundedView = snapshotRoundedView
}
presentedViewController.transitionCoordinator?.animate(
alongsideTransition: { [weak self] context in
guard let `self` = self else { return }
self.snapshotView?.transform = .identity
self.snapshotViewContainer.transform = finalTransform
self.gradeView.alpha = 0
if self.hapticMoments.contains(.willDismiss) {
self.feedbackGenerator.impactOccurred()
}
}, completion: { _ in
rootSnapshotView?.removeFromSuperview()
rootSnapshotRoundedView?.removeFromSuperview()
})
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
super.dismissalTransitionDidEnd(completed)
guard let containerView = containerView else { return }
self.backgroundView.removeFromSuperview()
self.snapshotView?.removeFromSuperview()
self.snapshotViewContainer.removeFromSuperview()
self.indicatorView.removeFromSuperview()
self.closeButton.removeFromSuperview()
let offscreenFrame = CGRect(x: 0, y: containerView.bounds.height, width: containerView.bounds.width, height: containerView.bounds.height)
presentedViewController.view.frame = offscreenFrame
presentedViewController.view.transform = .identity
}
}
extension SPStorkPresentationController {
@objc func 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(self.pan), self.swipeToDismissEnabled else { return }
switch gestureRecognizer.state {
case .began:
self.workGester = true
self.indicatorView.style = .line
self.presentingViewController.view.layer.removeAllAnimations()
self.presentingViewController.view.endEditing(true)
self.presentedViewController.view.endEditing(true)
gestureRecognizer.setTranslation(CGPoint(x: 0, y: 0), in: containerView)
case .changed:
self.workGester = true
let translation = gestureRecognizer.translation(in: presentedView)
if self.swipeToDismissEnabled {
self.updatePresentedViewForTranslation(inVerticalDirection: translation.y)
} else {
gestureRecognizer.setTranslation(.zero, in: presentedView)
}
case .ended:
self.workGester = false
let translation = gestureRecognizer.translation(in: presentedView).y
let toDefault = {
self.indicatorView.style = .arrow
UIView.animate(
withDuration: 0.6,
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: [.curveEaseOut, .allowUserInteraction],
animations: {
self.snapshotView?.transform = .identity
self.presentedView?.transform = .identity
self.gradeView.alpha = self.alpha
})
}
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)
}
}
func updatePresentingController() {
if self.startDismissing { return }
self.updateSnapshot()
}
func setIndicator(style: SPStorkIndicatorView.Style) {
self.indicatorView.style = style
}
func setIndicator(visible: Bool, forse: Bool) {
guard self.hideIndicatorWhenScroll else { return }
let newAlpha: CGFloat = visible ? 1 : 0
if forse {
self.indicatorView.layer.removeAllAnimations()
self.indicatorView.alpha = newAlpha
return
}
if self.indicatorView.alpha == newAlpha {
return
}
UIView.animate(withDuration: 0.18, animations: {
self.indicatorView.alpha = newAlpha
})
}
private func updatePresentedViewForTranslation(inVerticalDirection translation: CGFloat) {
if self.startDismissing { return }
let elasticThreshold: CGFloat = 120
let translationFactor: CGFloat = 1 / 2
if translation >= 0 {
let translationForModal: CGFloat = {
if translation >= elasticThreshold {
let frictionLength = translation - elasticThreshold
let frictionTranslation = 30 * atan(frictionLength / 120) + frictionLength / 10
return frictionTranslation + (elasticThreshold * translationFactor)
} else {
return translation * translationFactor
}
}()
self.presentedView?.transform = CGAffineTransform(translationX: 0, y: translationForModal)
let scaleFactor = 1 + (translationForModal / 5000)
self.snapshotView?.transform = CGAffineTransform.init(scaleX: scaleFactor, y: scaleFactor)
let gradeFactor = 1 + (translationForModal / 7000)
self.gradeView.alpha = self.alpha - ((gradeFactor - 1) * 15)
} else {
self.presentedView?.transform = CGAffineTransform.identity
}
if self.swipeToDismissEnabled {
let afterRealseDismissing = (translation >= self.translateForDismiss)
if afterRealseDismissing != self.afterReleaseDismissing {
self.afterReleaseDismissing = afterRealseDismissing
if !self.workConfirmation {
if self.hapticMoments.contains(.willDismissIfRelease) {
self.feedbackGenerator.impactOccurred()
}
}
}
}
}
}
extension SPStorkPresentationController {
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
guard let containerView = containerView else { return }
self.updateSnapshotAspectRatio()
if presentedViewController.view.isDescendant(of: containerView) {
self.presentedViewController.view.frame = self.frameOfPresentedViewInContainerView
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { contex in
self.updateLayoutIndicator()
self.updateLayoutCloseButton()
}, completion: { [weak self] _ in
self?.updateSnapshotAspectRatio()
self?.updateSnapshot()
})
}
private func updateLayoutIndicator() {
self.indicatorView.style = .line
self.indicatorView.sizeToFit()
self.indicatorView.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() {
guard let currentSnapshotView = presentingViewController.view.snapshotView(afterScreenUpdates: true) else { return }
self.snapshotView?.removeFromSuperview()
self.snapshotViewContainer.addSubview(currentSnapshotView)
self.constraints(view: currentSnapshotView, to: self.snapshotViewContainer)
self.snapshotView = currentSnapshotView
self.snapshotView?.layer.cornerRadius = self.cornerRadius
self.snapshotView?.layer.masksToBounds = true
if #available(iOS 11.0, *) {
snapshotView?.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
}
self.gradeView.removeFromSuperview()
self.gradeView.backgroundColor = UIColor.black
self.snapshotView!.addSubview(self.gradeView)
self.constraints(view: self.gradeView, to: self.snapshotView!)
}
private func updateSnapshotAspectRatio() {
guard let containerView = containerView, snapshotViewContainer.translatesAutoresizingMaskIntoConstraints == false else { return }
self.snapshotViewTopConstraint?.isActive = false
self.snapshotViewWidthConstraint?.isActive = false
self.snapshotViewAspectRatioConstraint?.isActive = false
let snapshotReferenceSize = presentingViewController.view.frame.size
let aspectRatio = snapshotReferenceSize.width / snapshotReferenceSize.height
self.snapshotViewTopConstraint = snapshotViewContainer.topAnchor.constraint(equalTo: containerView.topAnchor, constant: self.topSpace)
self.snapshotViewWidthConstraint = snapshotViewContainer.widthAnchor.constraint(equalTo: containerView.widthAnchor, multiplier: scaleForPresentingView)
self.snapshotViewAspectRatioConstraint = snapshotViewContainer.widthAnchor.constraint(equalTo: snapshotViewContainer.heightAnchor, multiplier: aspectRatio)
self.snapshotViewTopConstraint?.isActive = true
self.snapshotViewWidthConstraint?.isActive = true
self.snapshotViewAspectRatioConstraint?.isActive = true
}
private func constraints(view: UIView, to superView: UIView) {
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: superView.topAnchor),
view.leftAnchor.constraint(equalTo: superView.leftAnchor),
view.rightAnchor.constraint(equalTo: superView.rightAnchor),
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor)
])
}
private func addCornerRadiusAnimation(for view: UIView?, cornerRadius: CGFloat, duration: CFTimeInterval) {
guard let view = view else { return }
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
animation.fromValue = view.layer.cornerRadius
animation.toValue = cornerRadius
animation.duration = duration
view.layer.add(animation, forKey: "cornerRadius")
view.layer.cornerRadius = cornerRadius
}
}
@@ -0,0 +1,54 @@
// 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
final class SPStorkPresentingAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let presentedViewController = transitionContext.viewController(forKey: .to) else { return }
let containerView = transitionContext.containerView
containerView.addSubview(presentedViewController.view)
let finalFrameForPresentedView = transitionContext.finalFrame(for: presentedViewController)
presentedViewController.view.frame = finalFrameForPresentedView
presentedViewController.view.frame.origin.y = containerView.bounds.height
UIView.animate(
withDuration: transitionDuration(using: transitionContext),
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: .curveEaseOut,
animations: {
presentedViewController.view.frame = finalFrameForPresentedView
}, completion: { finished in
transitionContext.completeTransition(finished)
})
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.6
}
}
@@ -0,0 +1,66 @@
// 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 final class SPStorkTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
public var swipeToDismissEnabled: Bool = true
public var tapAroundToDismissEnabled: Bool = true
public var showCloseButton: Bool = false
public var showIndicator: Bool = true
public var indicatorColor: UIColor = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
public var hideIndicatorWhenScroll: Bool = false
public var 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.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
}
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return SPStorkPresentingAnimationController()
}
public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return SPStorkDismissingAnimationController()
}
}
@@ -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)
}
}
@@ -0,0 +1,50 @@
// 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 SPStorkCloseView: UIView {
var color = UIColor.blue {
didSet {
self.setNeedsDisplay()
}
}
init() {
super.init(frame: CGRect.zero)
self.commonInit()
}
private func commonInit() {
self.backgroundColor = UIColor.clear
}
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)
}
}
@@ -0,0 +1,115 @@
// 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 SPStorkIndicatorView: UIView {
var style: Style = .line {
didSet {
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
}
}
private var leftView: UIView = UIView()
private var rightView: UIView = UIView()
init() {
super.init(frame: .zero)
self.backgroundColor = UIColor.clear
self.addSubview(self.leftView)
self.addSubview(self.rightView)
self.color = UIColor.init(red: 202/255, green: 201/255, blue: 207/255, alpha: 1)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
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
let correction = height / 2
self.leftView.frame = CGRect.init(x: 0, y: 0, width: self.frame.width / 2 + correction, height: height)
self.leftView.center.y = self.frame.height / 2
self.leftView.layer.cornerRadius = min(self.leftView.frame.width, self.leftView.frame.height) / 2
self.rightView.frame = CGRect.init(x: self.frame.width / 2 - correction, y: 0, width: self.frame.width / 2 + correction, height: height)
self.rightView.center.y = self.frame.height / 2
self.rightView.layer.cornerRadius = min(self.leftView.frame.width, self.leftView.frame.height) / 2
}
private func animate(animations: @escaping (() -> Void)) {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.beginFromCurrentState, .curveEaseOut], animations: {
animations()
})
}
enum Style {
case arrow
case line
}
}
@@ -0,0 +1,65 @@
// 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 SPAnimation {
static func animate(_ duration: TimeInterval,
animations: (() -> Void)!,
delay: TimeInterval = 0,
options: UIView.AnimationOptions = [],
withComplection completion: (() -> Void)! = {}) {
UIView.animate(
withDuration: duration,
delay: delay,
options: options,
animations: {
animations()
}, completion: { finished in
completion()
})
}
static func animateWithRepeatition(_ duration: TimeInterval,
animations: (() -> Void)!,
delay: TimeInterval = 0,
options: UIView.AnimationOptions = [],
withComplection completion: (() -> Void)! = {}) {
var optionsWithRepeatition = options
optionsWithRepeatition.insert([.autoreverse, .repeat, .allowUserInteraction])
self.animate(
duration,
animations: {
animations()
},
delay: delay,
options: optionsWithRepeatition,
withComplection: {
completion()
})
}
}
@@ -0,0 +1,103 @@
// 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
class SPAnimationAlpha {
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],
delayPerItem: TimeInterval = delayPerItem,
withComplection completion: (() -> Void)! = {}) {
var del: Double = 0
for view in views {
delay(del, closure: {
if (view == views.last) {
SPAnimation.animate(duration, animations: {
view.alpha = 0
}, withComplection: {
completion()
})
} else {
SPAnimation.animate(duration, animations: {
view.alpha = 0
})
}
})
del += delayPerItem
}
}
static func hideReverseList(_ duration: TimeInterval = durationListAnimation,
views: [UIView],
delayPerItem: TimeInterval = delayPerItem,
withComplection completion: (() -> Void)! = {}) {
var del: Double = 0
let reversedViews = views.reversed()
for view in reversedViews {
delay(del, closure: {
if (view == views.last) {
SPAnimation.animate(duration, animations: {
view.alpha = 0
}, withComplection: {
completion()
})
} else {
SPAnimation.animate(duration, animations: {
view.alpha = 0
})
}
})
del += delayPerItem
}
}
static func showList(_ duration: TimeInterval = durationListAnimation,
views: [UIView],
delayPerItem: TimeInterval = delayPerItem,
withComplection completion: (() -> Void)! = {}) {
var del: Double = 0
for view in views {
delay(del, closure: {
if (view == views.last) {
SPAnimation.animate(duration, animations: {
view.alpha = 1
}, withComplection: {
completion()
})
} else {
SPAnimation.animate(duration, animations: {
view.alpha = 1
})
}
})
del += delayPerItem
}
}
}
@@ -0,0 +1,73 @@
// 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
class SPAnimationSpring {
static let spring: CGFloat = 1
static let velocity: CGFloat = 1
static func animate(_ duration: TimeInterval,
animations: (() -> Void)!,
delay: TimeInterval = 0,
spring: CGFloat = spring,
velocity: CGFloat = velocity,
options: UIView.AnimationOptions = [],
withComplection completion: (() -> Void)! = {}) {
UIView.animate(
withDuration: duration,
delay: delay,
usingSpringWithDamping: spring,
initialSpringVelocity: velocity,
options: options,
animations: {
animations()
}, completion: { finished in
completion()
})
}
static func animateWithRepeatition(_ duration: TimeInterval,
animations: (() -> Void)!,
delay: TimeInterval = 0,
spring: CGFloat = spring,
velocity: CGFloat = velocity,
options: UIView.AnimationOptions = [],
withComplection completion: (() -> Void)! = {}) {
var optionsWithRepeatition = options
optionsWithRepeatition.insert([.autoreverse, .repeat])
UIView.animate(
withDuration: duration,
delay: delay,
usingSpringWithDamping: spring,
initialSpringVelocity: velocity,
options: optionsWithRepeatition,
animations: {
animations()
}, completion: { finished in
completion()
})
}
}
@@ -0,0 +1,119 @@
// 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
class SPAnimationUpward {
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,
delay: TimeInterval = 0,
withComplection completion: (() -> Void)! = {}) {
var options: UIView.AnimationOptions = []
options.insert(.curveEaseIn)
SPAnimationSpring.animate(
duration, animations: {
view.alpha = 0
view.frame.origin.y = view.frame.origin.y + (-UIScreen.main.bounds.height / coefLenthForTransition)
},
delay: delay,
options: options,
withComplection: {
completion()
})
}
static func hideList(_ duration: TimeInterval = durationListAnimation,
views: [UIView],
delayPerItem: TimeInterval = delayPerItem,
withComplection completion: (() -> Void)! = {}) {
var del: Double = 0
for view in views {
delay(del, closure: {
if (view == views.last) {
SPAnimationUpward.hide(duration, view: view, withComplection: {
completion()
})
} else {
SPAnimationUpward.hide(duration, view: view)
}
})
del += delayPerItem
}
}
static func show(_ duration: TimeInterval,
view: UIView,
delay: TimeInterval = 0,
withComplection completion: (() -> Void)! = {}) {
view.alpha = 0
view.isHidden = false
view.transform = CGAffineTransform(translationX: 0, y: UIScreen.main.bounds.height / coefLenthForTransition)
var options: UIView.AnimationOptions = []
options.insert(.curveEaseOut)
SPAnimationSpring.animate(
duration, animations: {
view.alpha = 1
view.transform = CGAffineTransform.identity
},
delay: delay,
options: options,
withComplection: {
completion()
})
}
static func showList(_ duration: TimeInterval = durationListAnimation,
views: [UIView],
delayPerItem: TimeInterval = delayPerItem,
options: UIView.AnimationOptions = [],
withComplection completion: (() -> Void)! = {}) {
var del: Double = 0
for view in views {
delay(del, closure: {
if (view == views.last) {
SPAnimationUpward.show(duration, view: view, withComplection: {
completion()
})
} else {
SPAnimationUpward.show(duration, view: view)
}
})
del += delayPerItem
}
}
}
@@ -0,0 +1,73 @@
// 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 SPApp {
static var udid: String? {
return UIDevice.current.identifierForVendor?.uuidString
}
static var displayName: String? {
return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
}
static var rootController: UIViewController? {
return UIApplication.shared.keyWindow?.rootViewController
}
static var safeArea: UIEdgeInsets {
if #available(iOS 11.0, *) {
return UIApplication.shared.keyWindow?.safeArea ?? UIEdgeInsets.zero
} else {
return UIEdgeInsets.zero
}
}
static func set(rootController: UIViewController, animatable: Bool = true) {
rootController.view.frame = UIScreen.main.bounds
let replaceRootViewController = {
UIApplication.shared.keyWindow?.rootViewController = rootController
}
if animatable {
UIView.transition(
with: UIApplication.shared.keyWindow ?? UIWindow(),
duration: 0.5,
options: UIView.AnimationOptions.transitionCrossDissolve,
animations: {
replaceRootViewController()
}, completion: nil)
} else {
replaceRootViewController()
}
}
static func set(elementsColor: UIColor) {
UINavigationController.elementsColor = elementsColor
UIAlertController.elementsColor = elementsColor
UITabBarController.elementsColor = elementsColor
UITabBar.appearance().tintColor = elementsColor
}
}
@@ -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)})
}
@@ -0,0 +1,139 @@
// 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 StoreKit
struct SPAppStore {
static func link(appID: String) -> String {
return "https://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)
} else {
UIApplication.shared.openURL(url)
}
}
}
static func requestReview(appID: String, force: Bool) {
if force {
if let url = URL(string: "itms-apps://itunes.apple.com/us/app/apple-store/id\(appID)?mt=8&action=write-review"),
UIApplication.shared.canOpenURL(url) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
} else {
UIApplication.shared.openURL(url)
}
}
} else {
if #available(iOS 10.3, *) {
SKStoreReviewController.requestReview()
}
}
}
static func requestReview() {
if #available(iOS 10.3, *) {
SKStoreReviewController.requestReview()
}
}
static func isUpdateAvailable(completion: @escaping (Bool)->()) {
guard let info = Bundle.main.infoDictionary,
let currentVersion = info["CFBundleShortVersionString"] as? String,
let identifier = info["CFBundleIdentifier"] as? String,
let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
completion(false)
return
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
if let error = error { throw error }
guard let data = data else {
completion(false)
return
}
let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any]
guard let result = (json?["results"] as? [Any])?.first as? [String: Any], let version = result["version"] as? String else {
completion(false)
return
}
let compareResult = currentVersion.compare(version, options: .numeric)
DispatchQueue.main.async {
completion(compareResult == .orderedAscending)
}
} catch {
completion(false)
}
}
task.resume()
}
private init() {}
}
extension NSRange {
func range(for str: String) -> Range<String.Index>? {
guard location != NSNotFound else { return nil }
guard let fromUTFIndex = str.utf16.index(str.utf16.startIndex, offsetBy: location, limitedBy: str.utf16.endIndex) else { return nil }
guard let toUTFIndex = str.utf16.index(fromUTFIndex, offsetBy: length, limitedBy: str.utf16.endIndex) else { return nil }
guard let fromIndex = String.Index(fromUTFIndex, within: str) else { return nil }
guard let toIndex = String.Index(toUTFIndex, within: str) else { return nil }
return fromIndex ..< toIndex
}
}
extension String {
func ranges(of string: String, options: CompareOptions = .literal) -> [Range<Index>] {
var result: [Range<Index>] = []
var start = startIndex
while let range = range(of: string, options: options, range: start..<endIndex) {
result.append(range)
start = range.lowerBound < range.upperBound ? range.upperBound : index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return result
}
func slices(from: String, to: String) -> [Substring] {
let pattern = "(?<=" + from + ").*?(?=" + to + ")"
return ranges(of: pattern, options: .regularExpression)
.map{ self[$0] }
}
}
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
import AVFoundation
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() {}
}
fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String {
return input.rawValue
}
@@ -0,0 +1,57 @@
// 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 AVFoundation
class SPAudioPlayer: NSObject, AVAudioPlayerDelegate {
fileprivate var player: AVAudioPlayer = AVAudioPlayer()
fileprivate var endPlayingComplection: (()->())? = nil
func play(fileName: String, complection: (()->())? = nil, volume: Float = 1) {
self.endPlayingComplection?()
self.player = AVAudioPlayer()
let url = Bundle.main.url(forResource: fileName, withExtension: nil)
if url == nil {
self.endPlayingComplection?()
return
}
do {
self.player = try AVAudioPlayer(contentsOf: url!)
player.volume = volume
player.delegate = self
player.prepareToPlay()
player.play()
self.endPlayingComplection = complection
} catch let error as NSError {
print(error.description)
}
}
func stop() {
player.stop()
}
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
self.endPlayingComplection?()
}
}
@@ -0,0 +1,37 @@
// 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
struct SPBufer {
static var text: String? {
get {
return UIPasteboard.general.string
}
set {
UIPasteboard.general.string = newValue
}
}
private init() {}
}
@@ -0,0 +1,161 @@
// 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 SPCodeDraw {
class AudioIconPack : NSObject {
@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()!
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 200, height: 200), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 200, y: resizedFrame.height / 200)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 177.85, y: 86.98))
bezierPath.addLine(to: CGPoint(x: 54.26, y: 9.11))
bezierPath.addCurve(to: CGPoint(x: 42.64, y: 7.3), controlPoint1: CGPoint(x: 50.61, y: 6.94), controlPoint2: CGPoint(x: 46.07, y: 7.3))
bezierPath.addCurve(to: CGPoint(x: 29, y: 20.56), controlPoint1: CGPoint(x: 28.94, y: 7.3), controlPoint2: CGPoint(x: 29, y: 17.88))
bezierPath.addLine(to: CGPoint(x: 29, y: 179.72))
bezierPath.addCurve(to: CGPoint(x: 42.64, y: 192.98), controlPoint1: CGPoint(x: 29, y: 181.98), controlPoint2: CGPoint(x: 28.94, y: 192.98))
bezierPath.addCurve(to: CGPoint(x: 54.26, y: 191.16), controlPoint1: CGPoint(x: 46.07, y: 192.98), controlPoint2: CGPoint(x: 50.61, y: 193.34))
bezierPath.addLine(to: CGPoint(x: 177.85, y: 113.3))
bezierPath.addCurve(to: CGPoint(x: 186.24, y: 100.14), controlPoint1: CGPoint(x: 187.99, y: 107.26), controlPoint2: CGPoint(x: 186.24, y: 100.14))
bezierPath.addCurve(to: CGPoint(x: 177.85, y: 86.98), controlPoint1: CGPoint(x: 186.24, y: 100.14), controlPoint2: CGPoint(x: 187.99, y: 93.02))
bezierPath.close()
bezierPath.usesEvenOddFillRule = true
fillColor.setFill()
bezierPath.fill()
context.restoreGState()
}
@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()!
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 200, height: 200), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 200, y: resizedFrame.height / 200)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 70.11, y: 6.86))
bezierPath.addLine(to: CGPoint(x: 34.32, y: 6.86))
bezierPath.addCurve(to: CGPoint(x: 20, y: 21.18), controlPoint1: CGPoint(x: 26.41, y: 6.86), controlPoint2: CGPoint(x: 20, y: 13.27))
bezierPath.addLine(to: CGPoint(x: 20, y: 178.68))
bezierPath.addCurve(to: CGPoint(x: 34.32, y: 193), controlPoint1: CGPoint(x: 20, y: 186.59), controlPoint2: CGPoint(x: 26.41, y: 193))
bezierPath.addLine(to: CGPoint(x: 70.11, y: 193))
bezierPath.addCurve(to: CGPoint(x: 84.43, y: 178.68), controlPoint1: CGPoint(x: 78.02, y: 193), controlPoint2: CGPoint(x: 84.43, y: 186.59))
bezierPath.addLine(to: CGPoint(x: 84.43, y: 21.18))
bezierPath.addCurve(to: CGPoint(x: 70.11, y: 6.86), controlPoint1: CGPoint(x: 84.43, y: 13.27), controlPoint2: CGPoint(x: 78.02, y: 6.86))
bezierPath.close()
bezierPath.move(to: CGPoint(x: 163.19, y: 6.86))
bezierPath.addLine(to: CGPoint(x: 127.39, y: 6.86))
bezierPath.addCurve(to: CGPoint(x: 113.07, y: 21.18), controlPoint1: CGPoint(x: 119.48, y: 6.86), controlPoint2: CGPoint(x: 113.07, y: 13.27))
bezierPath.addLine(to: CGPoint(x: 113.07, y: 178.68))
bezierPath.addCurve(to: CGPoint(x: 127.39, y: 193), controlPoint1: CGPoint(x: 113.07, y: 186.59), controlPoint2: CGPoint(x: 119.48, y: 193))
bezierPath.addLine(to: CGPoint(x: 163.19, y: 193))
bezierPath.addCurve(to: CGPoint(x: 177.5, y: 178.68), controlPoint1: CGPoint(x: 171.09, y: 193), controlPoint2: CGPoint(x: 177.5, y: 186.59))
bezierPath.addLine(to: CGPoint(x: 177.5, y: 21.18))
bezierPath.addCurve(to: CGPoint(x: 163.19, y: 6.86), controlPoint1: CGPoint(x: 177.5, y: 13.27), controlPoint2: CGPoint(x: 171.09, y: 6.86))
bezierPath.close()
bezierPath.usesEvenOddFillRule = true
fillColor.setFill()
bezierPath.fill()
context.restoreGState()
}
@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()!
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 200, height: 200), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 200, y: resizedFrame.height / 200)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 177.82, y: 7.86))
bezierPath.addLine(to: CGPoint(x: 20.32, y: 7.86))
bezierPath.addCurve(to: CGPoint(x: 6, y: 22.18), controlPoint1: CGPoint(x: 12.41, y: 7.86), controlPoint2: CGPoint(x: 6, y: 14.27))
bezierPath.addLine(to: CGPoint(x: 6, y: 179.68))
bezierPath.addCurve(to: CGPoint(x: 20.32, y: 194), controlPoint1: CGPoint(x: 6, y: 187.59), controlPoint2: CGPoint(x: 12.41, y: 194))
bezierPath.addLine(to: CGPoint(x: 177.82, y: 194))
bezierPath.addCurve(to: CGPoint(x: 192.15, y: 179.68), controlPoint1: CGPoint(x: 185.73, y: 194), controlPoint2: CGPoint(x: 192.15, y: 187.59))
bezierPath.addLine(to: CGPoint(x: 192.15, y: 22.18))
bezierPath.addCurve(to: CGPoint(x: 177.82, y: 7.86), controlPoint1: CGPoint(x: 192.15, y: 14.27), controlPoint2: CGPoint(x: 185.73, y: 7.86))
bezierPath.close()
bezierPath.usesEvenOddFillRule = true
fillColor.setFill()
bezierPath.fill()
context.restoreGState()
}
@objc(StyleKitNameResizingBehavior)
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.
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,24 @@
// 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
struct SPCodeDraw { private init() {} }
@@ -0,0 +1,486 @@
// 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 SPCodeDraw {
class SocialIconPack : NSObject {
@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()!
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 40, height: 40), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 40, y: resizedFrame.height / 40)
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 19.9, y: 0.99))
bezierPath.addCurve(to: CGPoint(x: 12.12, y: 1.1), controlPoint1: CGPoint(x: 14.77, y: 0.99), controlPoint2: CGPoint(x: 14.13, y: 1.01))
bezierPath.addCurve(to: CGPoint(x: 7.54, y: 1.98), controlPoint1: CGPoint(x: 10.11, y: 1.19), controlPoint2: CGPoint(x: 8.74, y: 1.51))
bezierPath.addCurve(to: CGPoint(x: 4.19, y: 4.15), controlPoint1: CGPoint(x: 6.29, y: 2.46), controlPoint2: CGPoint(x: 5.24, y: 3.1))
bezierPath.addCurve(to: CGPoint(x: 2.02, y: 7.5), controlPoint1: CGPoint(x: 3.14, y: 5.2), controlPoint2: CGPoint(x: 2.5, y: 6.26))
bezierPath.addCurve(to: CGPoint(x: 1.14, y: 12.08), controlPoint1: CGPoint(x: 1.55, y: 8.7), controlPoint2: CGPoint(x: 1.23, y: 10.07))
bezierPath.addCurve(to: CGPoint(x: 1.03, y: 19.86), controlPoint1: CGPoint(x: 1.05, y: 14.09), controlPoint2: CGPoint(x: 1.03, y: 14.73))
bezierPath.addCurve(to: CGPoint(x: 1.14, y: 27.64), controlPoint1: CGPoint(x: 1.03, y: 24.98), controlPoint2: CGPoint(x: 1.05, y: 25.63))
bezierPath.addCurve(to: CGPoint(x: 2.02, y: 32.22), controlPoint1: CGPoint(x: 1.23, y: 29.65), controlPoint2: CGPoint(x: 1.55, y: 31.02))
bezierPath.addCurve(to: CGPoint(x: 4.19, y: 35.56), controlPoint1: CGPoint(x: 2.5, y: 33.46), controlPoint2: CGPoint(x: 3.14, y: 34.51))
bezierPath.addCurve(to: CGPoint(x: 7.54, y: 37.74), controlPoint1: CGPoint(x: 5.24, y: 36.61), controlPoint2: CGPoint(x: 6.29, y: 37.26))
bezierPath.addCurve(to: CGPoint(x: 12.12, y: 38.62), controlPoint1: CGPoint(x: 8.74, y: 38.21), controlPoint2: CGPoint(x: 10.11, y: 38.52))
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 38.73), controlPoint1: CGPoint(x: 14.13, y: 38.71), controlPoint2: CGPoint(x: 14.77, y: 38.73))
bezierPath.addCurve(to: CGPoint(x: 27.68, y: 38.62), controlPoint1: CGPoint(x: 25.02, y: 38.73), controlPoint2: CGPoint(x: 25.67, y: 38.71))
bezierPath.addCurve(to: CGPoint(x: 32.26, y: 37.74), controlPoint1: CGPoint(x: 29.69, y: 38.52), controlPoint2: CGPoint(x: 31.06, y: 38.21))
bezierPath.addCurve(to: CGPoint(x: 35.6, y: 35.56), controlPoint1: CGPoint(x: 33.5, y: 37.26), controlPoint2: CGPoint(x: 34.55, y: 36.61))
bezierPath.addCurve(to: CGPoint(x: 37.78, y: 32.22), controlPoint1: CGPoint(x: 36.65, y: 34.51), controlPoint2: CGPoint(x: 37.3, y: 33.46))
bezierPath.addCurve(to: CGPoint(x: 38.66, y: 27.64), controlPoint1: CGPoint(x: 38.25, y: 31.02), controlPoint2: CGPoint(x: 38.56, y: 29.65))
bezierPath.addCurve(to: CGPoint(x: 38.77, y: 19.86), controlPoint1: CGPoint(x: 38.75, y: 25.63), controlPoint2: CGPoint(x: 38.77, y: 24.98))
bezierPath.addCurve(to: CGPoint(x: 38.66, y: 12.08), controlPoint1: CGPoint(x: 38.77, y: 14.73), controlPoint2: CGPoint(x: 38.75, y: 14.09))
bezierPath.addCurve(to: CGPoint(x: 37.78, y: 7.5), controlPoint1: CGPoint(x: 38.56, y: 10.07), controlPoint2: CGPoint(x: 38.25, y: 8.7))
bezierPath.addCurve(to: CGPoint(x: 35.6, y: 4.15), controlPoint1: CGPoint(x: 37.3, y: 6.26), controlPoint2: CGPoint(x: 36.65, y: 5.2))
bezierPath.addCurve(to: CGPoint(x: 32.26, y: 1.98), controlPoint1: CGPoint(x: 34.55, y: 3.1), controlPoint2: CGPoint(x: 33.5, y: 2.46))
bezierPath.addCurve(to: CGPoint(x: 27.68, y: 1.1), controlPoint1: CGPoint(x: 31.06, y: 1.51), controlPoint2: CGPoint(x: 29.69, y: 1.19))
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 0.99), controlPoint1: CGPoint(x: 25.67, y: 1.01), controlPoint2: CGPoint(x: 25.02, y: 0.99))
bezierPath.close()
bezierPath.move(to: CGPoint(x: 19.9, y: 4.39))
bezierPath.addCurve(to: CGPoint(x: 27.52, y: 4.5), controlPoint1: CGPoint(x: 24.94, y: 4.39), controlPoint2: CGPoint(x: 25.53, y: 4.41))
bezierPath.addCurve(to: CGPoint(x: 31.03, y: 5.15), controlPoint1: CGPoint(x: 29.36, y: 4.58), controlPoint2: CGPoint(x: 30.36, y: 4.89))
bezierPath.addCurve(to: CGPoint(x: 33.2, y: 6.56), controlPoint1: CGPoint(x: 31.91, y: 5.49), controlPoint2: CGPoint(x: 32.54, y: 5.9))
bezierPath.addCurve(to: CGPoint(x: 34.61, y: 8.73), controlPoint1: CGPoint(x: 33.86, y: 7.22), controlPoint2: CGPoint(x: 34.27, y: 7.85))
bezierPath.addCurve(to: CGPoint(x: 35.26, y: 12.23), controlPoint1: CGPoint(x: 34.87, y: 9.39), controlPoint2: CGPoint(x: 35.18, y: 10.39))
bezierPath.addCurve(to: CGPoint(x: 35.37, y: 19.86), controlPoint1: CGPoint(x: 35.35, y: 14.22), controlPoint2: CGPoint(x: 35.37, y: 14.82))
bezierPath.addCurve(to: CGPoint(x: 35.26, y: 27.48), controlPoint1: CGPoint(x: 35.37, y: 24.9), controlPoint2: CGPoint(x: 35.35, y: 25.49))
bezierPath.addCurve(to: CGPoint(x: 34.61, y: 30.99), controlPoint1: CGPoint(x: 35.18, y: 29.32), controlPoint2: CGPoint(x: 34.87, y: 30.32))
bezierPath.addCurve(to: CGPoint(x: 33.2, y: 33.16), controlPoint1: CGPoint(x: 34.27, y: 31.87), controlPoint2: CGPoint(x: 33.86, y: 32.5))
bezierPath.addCurve(to: CGPoint(x: 31.03, y: 34.57), controlPoint1: CGPoint(x: 32.54, y: 33.82), controlPoint2: CGPoint(x: 31.91, y: 34.23))
bezierPath.addCurve(to: CGPoint(x: 27.52, y: 35.22), controlPoint1: CGPoint(x: 30.36, y: 34.83), controlPoint2: CGPoint(x: 29.36, y: 35.14))
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 35.33), controlPoint1: CGPoint(x: 25.53, y: 35.31), controlPoint2: CGPoint(x: 24.94, y: 35.33))
bezierPath.addCurve(to: CGPoint(x: 12.27, y: 35.22), controlPoint1: CGPoint(x: 14.86, y: 35.33), controlPoint2: CGPoint(x: 14.26, y: 35.31))
bezierPath.addCurve(to: CGPoint(x: 8.77, y: 34.57), controlPoint1: CGPoint(x: 10.43, y: 35.14), controlPoint2: CGPoint(x: 9.43, y: 34.83))
bezierPath.addCurve(to: CGPoint(x: 6.6, y: 33.16), controlPoint1: CGPoint(x: 7.89, y: 34.23), controlPoint2: CGPoint(x: 7.26, y: 33.82))
bezierPath.addCurve(to: CGPoint(x: 5.19, y: 30.99), controlPoint1: CGPoint(x: 5.94, y: 32.5), controlPoint2: CGPoint(x: 5.53, y: 31.87))
bezierPath.addCurve(to: CGPoint(x: 4.54, y: 27.48), controlPoint1: CGPoint(x: 4.93, y: 30.32), controlPoint2: CGPoint(x: 4.62, y: 29.32))
bezierPath.addCurve(to: CGPoint(x: 4.43, y: 19.86), controlPoint1: CGPoint(x: 4.45, y: 25.49), controlPoint2: CGPoint(x: 4.43, y: 24.9))
bezierPath.addCurve(to: CGPoint(x: 4.54, y: 12.23), controlPoint1: CGPoint(x: 4.43, y: 14.82), controlPoint2: CGPoint(x: 4.45, y: 14.22))
bezierPath.addCurve(to: CGPoint(x: 5.19, y: 8.73), controlPoint1: CGPoint(x: 4.62, y: 10.39), controlPoint2: CGPoint(x: 4.93, y: 9.39))
bezierPath.addCurve(to: CGPoint(x: 6.6, y: 6.56), controlPoint1: CGPoint(x: 5.53, y: 7.85), controlPoint2: CGPoint(x: 5.94, y: 7.22))
bezierPath.addCurve(to: CGPoint(x: 8.77, y: 5.15), controlPoint1: CGPoint(x: 7.26, y: 5.9), controlPoint2: CGPoint(x: 7.89, y: 5.49))
bezierPath.addCurve(to: CGPoint(x: 12.27, y: 4.5), controlPoint1: CGPoint(x: 9.43, y: 4.89), controlPoint2: CGPoint(x: 10.43, y: 4.58))
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 4.39), controlPoint1: CGPoint(x: 14.26, y: 4.41), controlPoint2: CGPoint(x: 14.86, y: 4.39))
bezierPath.addLine(to: CGPoint(x: 19.9, y: 4.39))
bezierPath.close()
bezierPath.move(to: CGPoint(x: 19.9, y: 26.15))
bezierPath.addCurve(to: CGPoint(x: 13.61, y: 19.86), controlPoint1: CGPoint(x: 16.42, y: 26.15), controlPoint2: CGPoint(x: 13.61, y: 23.33))
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 13.57), controlPoint1: CGPoint(x: 13.61, y: 16.38), controlPoint2: CGPoint(x: 16.42, y: 13.57))
bezierPath.addCurve(to: CGPoint(x: 26.19, y: 19.86), controlPoint1: CGPoint(x: 23.37, y: 13.57), controlPoint2: CGPoint(x: 26.19, y: 16.38))
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 26.15), controlPoint1: CGPoint(x: 26.19, y: 23.33), controlPoint2: CGPoint(x: 23.37, y: 26.15))
bezierPath.close()
bezierPath.move(to: CGPoint(x: 19.9, y: 10.17))
bezierPath.addCurve(to: CGPoint(x: 10.21, y: 19.86), controlPoint1: CGPoint(x: 14.55, y: 10.17), controlPoint2: CGPoint(x: 10.21, y: 14.51))
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 29.55), controlPoint1: CGPoint(x: 10.21, y: 25.21), controlPoint2: CGPoint(x: 14.55, y: 29.55))
bezierPath.addCurve(to: CGPoint(x: 29.59, y: 19.86), controlPoint1: CGPoint(x: 25.25, y: 29.55), controlPoint2: CGPoint(x: 29.59, y: 25.21))
bezierPath.addCurve(to: CGPoint(x: 19.9, y: 10.17), controlPoint1: CGPoint(x: 29.59, y: 14.51), controlPoint2: CGPoint(x: 25.25, y: 10.17))
bezierPath.addLine(to: CGPoint(x: 19.9, y: 10.17))
bezierPath.close()
bezierPath.move(to: CGPoint(x: 32.43, y: 9.57))
bezierPath.addCurve(to: CGPoint(x: 30.23, y: 11.77), controlPoint1: CGPoint(x: 32.43, y: 10.79), controlPoint2: CGPoint(x: 31.44, y: 11.77))
bezierPath.addCurve(to: CGPoint(x: 28.03, y: 9.57), controlPoint1: CGPoint(x: 29.01, y: 11.77), controlPoint2: CGPoint(x: 28.03, y: 10.79))
bezierPath.addCurve(to: CGPoint(x: 30.23, y: 7.38), controlPoint1: CGPoint(x: 28.03, y: 8.36), controlPoint2: CGPoint(x: 29.01, y: 7.38))
bezierPath.addCurve(to: CGPoint(x: 32.43, y: 9.57), controlPoint1: CGPoint(x: 31.44, y: 7.38), controlPoint2: CGPoint(x: 32.43, y: 8.36))
bezierPath.close()
fillColor.setFill()
bezierPath.fill()
context.restoreGState()
}
@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()!
//// Resize to Target Frame
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 40, height: 40), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 40, y: resizedFrame.height / 40)
//// Bezier Drawing
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 32.57, y: 19.79))
bezierPath.addCurve(to: CGPoint(x: 38.07, y: 10.51), controlPoint1: CGPoint(x: 32.57, y: 19.79), controlPoint2: CGPoint(x: 37.57, y: 12.8))
bezierPath.addCurve(to: CGPoint(x: 37.01, y: 9.23), controlPoint1: CGPoint(x: 38.24, y: 9.69), controlPoint2: CGPoint(x: 37.87, y: 9.23))
bezierPath.addCurve(to: CGPoint(x: 32.67, y: 9.23), controlPoint1: CGPoint(x: 37.01, y: 9.23), controlPoint2: CGPoint(x: 34.13, y: 9.23))
bezierPath.addCurve(to: CGPoint(x: 31.01, y: 10.28), controlPoint1: CGPoint(x: 31.67, y: 9.23), controlPoint2: CGPoint(x: 31.31, y: 9.66))
bezierPath.addCurve(to: CGPoint(x: 25.8, y: 18.34), controlPoint1: CGPoint(x: 31.01, y: 10.28), controlPoint2: CGPoint(x: 28.66, y: 15.23))
bezierPath.addCurve(to: CGPoint(x: 23.91, y: 19.66), controlPoint1: CGPoint(x: 24.89, y: 19.35), controlPoint2: CGPoint(x: 24.42, y: 19.66))
bezierPath.addCurve(to: CGPoint(x: 23.32, y: 18.41), controlPoint1: CGPoint(x: 23.5, y: 19.66), controlPoint2: CGPoint(x: 23.32, y: 19.32))
bezierPath.addLine(to: CGPoint(x: 23.32, y: 10.44))
bezierPath.addCurve(to: CGPoint(x: 22.25, y: 9), controlPoint1: CGPoint(x: 23.32, y: 9.33), controlPoint2: CGPoint(x: 23.18, y: 9))
bezierPath.addLine(to: CGPoint(x: 15.29, y: 9))
bezierPath.addCurve(to: CGPoint(x: 14.43, y: 9.72), controlPoint1: CGPoint(x: 14.76, y: 9), controlPoint2: CGPoint(x: 14.43, y: 9.31))
bezierPath.addCurve(to: CGPoint(x: 16.05, y: 13.85), controlPoint1: CGPoint(x: 14.43, y: 10.77), controlPoint2: CGPoint(x: 16.05, y: 11.01))
bezierPath.addLine(to: CGPoint(x: 16.05, y: 19.72))
bezierPath.addCurve(to: CGPoint(x: 15.42, y: 21.36), controlPoint1: CGPoint(x: 16.05, y: 20.9), controlPoint2: CGPoint(x: 15.99, y: 21.36))
bezierPath.addCurve(to: CGPoint(x: 8.39, y: 10.51), controlPoint1: CGPoint(x: 13.93, y: 21.36), controlPoint2: CGPoint(x: 10.38, y: 16.28))
bezierPath.addCurve(to: CGPoint(x: 6.44, y: 9), controlPoint1: CGPoint(x: 8, y: 9.33), controlPoint2: CGPoint(x: 7.57, y: 9))
bezierPath.addLine(to: CGPoint(x: 2.09, y: 9))
bezierPath.addCurve(to: CGPoint(x: 1, y: 10.05), controlPoint1: CGPoint(x: 1.46, y: 9), controlPoint2: CGPoint(x: 1, y: 9.43))
bezierPath.addCurve(to: CGPoint(x: 7.7, y: 23.62), controlPoint1: CGPoint(x: 1, y: 11.2), controlPoint2: CGPoint(x: 2.36, y: 16.51))
bezierPath.addCurve(to: CGPoint(x: 20.23, y: 31), controlPoint1: CGPoint(x: 11.28, y: 28.41), controlPoint2: CGPoint(x: 15.99, y: 31))
bezierPath.addCurve(to: CGPoint(x: 23.45, y: 29.49), controlPoint1: CGPoint(x: 22.82, y: 31), controlPoint2: CGPoint(x: 23.45, y: 30.57))
bezierPath.addLine(to: CGPoint(x: 23.45, y: 25.82))
bezierPath.addCurve(to: CGPoint(x: 24.34, y: 24.51), controlPoint1: CGPoint(x: 23.45, y: 24.9), controlPoint2: CGPoint(x: 23.81, y: 24.51))
bezierPath.addCurve(to: CGPoint(x: 28.46, y: 27.1), controlPoint1: CGPoint(x: 24.94, y: 24.51), controlPoint2: CGPoint(x: 25.99, y: 24.7))
bezierPath.addCurve(to: CGPoint(x: 33.16, y: 31), controlPoint1: CGPoint(x: 31.37, y: 29.85), controlPoint2: CGPoint(x: 31.57, y: 31))
bezierPath.addLine(to: CGPoint(x: 38.04, y: 31))
bezierPath.addCurve(to: CGPoint(x: 39, y: 29.95), controlPoint1: CGPoint(x: 38.54, y: 31), controlPoint2: CGPoint(x: 39, y: 30.77))
bezierPath.addCurve(to: CGPoint(x: 35.39, y: 24.64), controlPoint1: CGPoint(x: 39, y: 28.87), controlPoint2: CGPoint(x: 37.57, y: 26.93))
bezierPath.addCurve(to: CGPoint(x: 32.57, y: 21.59), controlPoint1: CGPoint(x: 34.49, y: 23.46), controlPoint2: CGPoint(x: 33.03, y: 22.18))
bezierPath.addCurve(to: CGPoint(x: 32.57, y: 19.79), controlPoint1: CGPoint(x: 31.9, y: 20.9), controlPoint2: CGPoint(x: 32.1, y: 20.51))
bezierPath.close()
bezierPath.usesEvenOddFillRule = true
fillColor.setFill()
bezierPath.fill()
context.restoreGState()
}
@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()!
//// Resize to Target Frame
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 40, height: 40), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 40, y: resizedFrame.height / 40)
//// Bezier Drawing
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 28.52, y: 22.97))
bezierPath.addCurve(to: CGPoint(x: 25.54, y: 21.56), controlPoint1: CGPoint(x: 28.08, y: 22.76), controlPoint2: CGPoint(x: 25.94, y: 21.71))
bezierPath.addCurve(to: CGPoint(x: 24.56, y: 21.78), controlPoint1: CGPoint(x: 25.14, y: 21.42), controlPoint2: CGPoint(x: 24.85, y: 21.35))
bezierPath.addCurve(to: CGPoint(x: 23.18, y: 23.48), controlPoint1: CGPoint(x: 24.27, y: 22.21), controlPoint2: CGPoint(x: 23.44, y: 23.19))
bezierPath.addCurve(to: CGPoint(x: 22.24, y: 23.59), controlPoint1: CGPoint(x: 22.93, y: 23.77), controlPoint2: CGPoint(x: 22.68, y: 23.81))
bezierPath.addCurve(to: CGPoint(x: 18.74, y: 21.44), controlPoint1: CGPoint(x: 21.81, y: 23.37), controlPoint2: CGPoint(x: 20.4, y: 22.91))
bezierPath.addCurve(to: CGPoint(x: 16.32, y: 18.44), controlPoint1: CGPoint(x: 17.44, y: 20.29), controlPoint2: CGPoint(x: 16.57, y: 18.87))
bezierPath.addCurve(to: CGPoint(x: 16.51, y: 17.55), controlPoint1: CGPoint(x: 16.06, y: 18), controlPoint2: CGPoint(x: 16.29, y: 17.77))
bezierPath.addCurve(to: CGPoint(x: 17.16, y: 16.79), controlPoint1: CGPoint(x: 16.7, y: 17.36), controlPoint2: CGPoint(x: 16.94, y: 17.05))
bezierPath.addCurve(to: CGPoint(x: 17.6, y: 16.07), controlPoint1: CGPoint(x: 17.38, y: 16.54), controlPoint2: CGPoint(x: 17.45, y: 16.36))
bezierPath.addCurve(to: CGPoint(x: 17.56, y: 15.31), controlPoint1: CGPoint(x: 17.74, y: 15.78), controlPoint2: CGPoint(x: 17.67, y: 15.53))
bezierPath.addCurve(to: CGPoint(x: 16.22, y: 12.09), controlPoint1: CGPoint(x: 17.45, y: 15.09), controlPoint2: CGPoint(x: 16.58, y: 12.96))
bezierPath.addCurve(to: CGPoint(x: 15.24, y: 11.35), controlPoint1: CGPoint(x: 15.86, y: 11.25), controlPoint2: CGPoint(x: 15.51, y: 11.36))
bezierPath.addCurve(to: CGPoint(x: 14.4, y: 11.33), controlPoint1: CGPoint(x: 14.98, y: 11.34), controlPoint2: CGPoint(x: 14.69, y: 11.33))
bezierPath.addCurve(to: CGPoint(x: 13.24, y: 11.88), controlPoint1: CGPoint(x: 14.11, y: 11.33), controlPoint2: CGPoint(x: 13.64, y: 11.44))
bezierPath.addCurve(to: CGPoint(x: 11.72, y: 15.49), controlPoint1: CGPoint(x: 12.84, y: 12.31), controlPoint2: CGPoint(x: 11.72, y: 13.36))
bezierPath.addCurve(to: CGPoint(x: 13.5, y: 19.97), controlPoint1: CGPoint(x: 11.72, y: 17.62), controlPoint2: CGPoint(x: 13.28, y: 19.68))
bezierPath.addCurve(to: CGPoint(x: 20.93, y: 26.52), controlPoint1: CGPoint(x: 13.71, y: 20.26), controlPoint2: CGPoint(x: 16.57, y: 24.64))
bezierPath.addCurve(to: CGPoint(x: 23.42, y: 27.43), controlPoint1: CGPoint(x: 21.97, y: 26.96), controlPoint2: CGPoint(x: 22.78, y: 27.23))
bezierPath.addCurve(to: CGPoint(x: 26.16, y: 27.6), controlPoint1: CGPoint(x: 24.46, y: 27.76), controlPoint2: CGPoint(x: 25.41, y: 27.71))
bezierPath.addCurve(to: CGPoint(x: 29.1, y: 25.54), controlPoint1: CGPoint(x: 27, y: 27.48), controlPoint2: CGPoint(x: 28.74, y: 26.55))
bezierPath.addCurve(to: CGPoint(x: 29.35, y: 23.48), controlPoint1: CGPoint(x: 29.46, y: 24.53), controlPoint2: CGPoint(x: 29.46, y: 23.66))
bezierPath.addCurve(to: CGPoint(x: 28.52, y: 22.97), controlPoint1: CGPoint(x: 29.24, y: 23.3), controlPoint2: CGPoint(x: 28.95, y: 23.19))
bezierPath.close()
bezierPath.move(to: CGPoint(x: 20.57, y: 33.77))
bezierPath.addLine(to: CGPoint(x: 20.57, y: 33.77))
bezierPath.addCurve(to: CGPoint(x: 13.2, y: 31.76), controlPoint1: CGPoint(x: 17.97, y: 33.77), controlPoint2: CGPoint(x: 15.42, y: 33.07))
bezierPath.addLine(to: CGPoint(x: 12.67, y: 31.45))
bezierPath.addLine(to: CGPoint(x: 7.18, y: 32.88))
bezierPath.addLine(to: CGPoint(x: 8.65, y: 27.56))
bezierPath.addLine(to: CGPoint(x: 8.3, y: 27.01))
bezierPath.addCurve(to: CGPoint(x: 6.09, y: 19.34), controlPoint1: CGPoint(x: 6.85, y: 24.72), controlPoint2: CGPoint(x: 6.09, y: 22.07))
bezierPath.addCurve(to: CGPoint(x: 20.58, y: 4.93), controlPoint1: CGPoint(x: 6.09, y: 11.4), controlPoint2: CGPoint(x: 12.59, y: 4.93))
bezierPath.addCurve(to: CGPoint(x: 30.82, y: 9.16), controlPoint1: CGPoint(x: 24.45, y: 4.93), controlPoint2: CGPoint(x: 28.08, y: 6.43))
bezierPath.addCurve(to: CGPoint(x: 35.06, y: 19.35), controlPoint1: CGPoint(x: 33.55, y: 11.88), controlPoint2: CGPoint(x: 35.06, y: 15.5))
bezierPath.addCurve(to: CGPoint(x: 20.57, y: 33.77), controlPoint1: CGPoint(x: 35.05, y: 27.3), controlPoint2: CGPoint(x: 28.56, y: 33.77))
bezierPath.close()
bezierPath.move(to: CGPoint(x: 32.9, y: 7.09))
bezierPath.addCurve(to: CGPoint(x: 20.57, y: 2), controlPoint1: CGPoint(x: 29.61, y: 3.81), controlPoint2: CGPoint(x: 25.23, y: 2))
bezierPath.addCurve(to: CGPoint(x: 3.15, y: 19.34), controlPoint1: CGPoint(x: 10.97, y: 2), controlPoint2: CGPoint(x: 3.15, y: 9.78))
bezierPath.addCurve(to: CGPoint(x: 5.47, y: 28.01), controlPoint1: CGPoint(x: 3.14, y: 22.4), controlPoint2: CGPoint(x: 3.95, y: 25.38))
bezierPath.addLine(to: CGPoint(x: 3, y: 37))
bezierPath.addLine(to: CGPoint(x: 12.24, y: 34.59))
bezierPath.addCurve(to: CGPoint(x: 20.57, y: 36.7), controlPoint1: CGPoint(x: 14.78, y: 35.97), controlPoint2: CGPoint(x: 17.65, y: 36.7))
bezierPath.addLine(to: CGPoint(x: 20.57, y: 36.7))
bezierPath.addLine(to: CGPoint(x: 20.57, y: 36.7))
bezierPath.addCurve(to: CGPoint(x: 38, y: 19.36), controlPoint1: CGPoint(x: 30.18, y: 36.7), controlPoint2: CGPoint(x: 38, y: 28.92))
bezierPath.addCurve(to: CGPoint(x: 32.9, y: 7.09), controlPoint1: CGPoint(x: 38, y: 14.72), controlPoint2: CGPoint(x: 36.19, y: 10.36))
bezierPath.close()
bezierPath.usesEvenOddFillRule = true
fillColor.setFill()
bezierPath.fill()
context.restoreGState()
}
@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()!
//// Resize to Target Frame
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 40, height: 40), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 40, y: resizedFrame.height / 40)
//// Bezier Drawing
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 35.15, y: 4.5))
bezierPath.addCurve(to: CGPoint(x: 33.94, y: 4.79), controlPoint1: CGPoint(x: 34.74, y: 4.51), controlPoint2: CGPoint(x: 34.32, y: 4.62))
bezierPath.addCurve(to: CGPoint(x: 18.74, y: 10.7), controlPoint1: CGPoint(x: 33.25, y: 5.06), controlPoint2: CGPoint(x: 26, y: 7.88))
bezierPath.addCurve(to: CGPoint(x: 8.69, y: 14.6), controlPoint1: CGPoint(x: 15.11, y: 12.12), controlPoint2: CGPoint(x: 11.49, y: 13.52))
bezierPath.addCurve(to: CGPoint(x: 3.67, y: 16.54), controlPoint1: CGPoint(x: 5.9, y: 15.69), controlPoint2: CGPoint(x: 3.87, y: 16.47))
bezierPath.addCurve(to: CGPoint(x: 1.4, y: 17.91), controlPoint1: CGPoint(x: 3.01, y: 16.77), controlPoint2: CGPoint(x: 2.05, y: 17.15))
bezierPath.addCurve(to: CGPoint(x: 1.12, y: 19.55), controlPoint1: CGPoint(x: 1.08, y: 18.28), controlPoint2: CGPoint(x: 0.84, y: 18.97))
bezierPath.addCurve(to: CGPoint(x: 2.72, y: 20.75), controlPoint1: CGPoint(x: 1.41, y: 20.13), controlPoint2: CGPoint(x: 1.95, y: 20.46))
bezierPath.addLine(to: CGPoint(x: 2.74, y: 20.76))
bezierPath.addLine(to: CGPoint(x: 2.76, y: 20.77))
bezierPath.addCurve(to: CGPoint(x: 10.35, y: 23.15), controlPoint1: CGPoint(x: 5.56, y: 21.64), controlPoint2: CGPoint(x: 9.78, y: 22.97))
bezierPath.addCurve(to: CGPoint(x: 13.25, y: 32.72), controlPoint1: CGPoint(x: 10.5, y: 23.63), controlPoint2: CGPoint(x: 12.28, y: 29.53))
bezierPath.addCurve(to: CGPoint(x: 14.93, y: 33.73), controlPoint1: CGPoint(x: 13.5, y: 33.38), controlPoint2: CGPoint(x: 14.24, y: 33.82))
bezierPath.addCurve(to: CGPoint(x: 15.53, y: 33.67), controlPoint1: CGPoint(x: 15.05, y: 33.73), controlPoint2: CGPoint(x: 15.28, y: 33.73))
bezierPath.addCurve(to: CGPoint(x: 16.83, y: 32.94), controlPoint1: CGPoint(x: 15.9, y: 33.58), controlPoint2: CGPoint(x: 16.38, y: 33.37))
bezierPath.addLine(to: CGPoint(x: 16.83, y: 32.94))
bezierPath.addCurve(to: CGPoint(x: 20.46, y: 29.4), controlPoint1: CGPoint(x: 17.48, y: 32.33), controlPoint2: CGPoint(x: 19.78, y: 30.07))
bezierPath.addLine(to: CGPoint(x: 27.96, y: 34.96))
bezierPath.addLine(to: CGPoint(x: 27.99, y: 34.98))
bezierPath.addCurve(to: CGPoint(x: 29.57, y: 35.49), controlPoint1: CGPoint(x: 27.99, y: 34.98), controlPoint2: CGPoint(x: 28.67, y: 35.43))
bezierPath.addCurve(to: CGPoint(x: 31.04, y: 35.09), controlPoint1: CGPoint(x: 30.01, y: 35.52), controlPoint2: CGPoint(x: 30.56, y: 35.44))
bezierPath.addCurve(to: CGPoint(x: 32.02, y: 33.42), controlPoint1: CGPoint(x: 31.51, y: 34.75), controlPoint2: CGPoint(x: 31.85, y: 34.17))
bezierPath.addCurve(to: CGPoint(x: 37.28, y: 8.61), controlPoint1: CGPoint(x: 32.61, y: 30.86), controlPoint2: CGPoint(x: 36.6, y: 11.79))
bezierPath.addCurve(to: CGPoint(x: 36.71, y: 5.03), controlPoint1: CGPoint(x: 37.71, y: 6.99), controlPoint2: CGPoint(x: 37.51, y: 5.73))
bezierPath.addCurve(to: CGPoint(x: 35.33, y: 4.5), controlPoint1: CGPoint(x: 36.3, y: 4.68), controlPoint2: CGPoint(x: 35.82, y: 4.52))
bezierPath.addCurve(to: CGPoint(x: 35.15, y: 4.5), controlPoint1: CGPoint(x: 35.27, y: 4.5), controlPoint2: CGPoint(x: 35.21, y: 4.5))
bezierPath.addLine(to: CGPoint(x: 35.15, y: 4.5))
bezierPath.close()
bezierPath.move(to: CGPoint(x: 35.24, y: 6.07))
bezierPath.addCurve(to: CGPoint(x: 35.68, y: 6.21), controlPoint1: CGPoint(x: 35.43, y: 6.07), controlPoint2: CGPoint(x: 35.59, y: 6.12))
bezierPath.addCurve(to: CGPoint(x: 35.77, y: 8.23), controlPoint1: CGPoint(x: 35.88, y: 6.38), controlPoint2: CGPoint(x: 36.13, y: 6.89))
bezierPath.addCurve(to: CGPoint(x: 30.51, y: 33.07), controlPoint1: CGPoint(x: 33.84, y: 16.86), controlPoint2: CGPoint(x: 32.16, y: 25.68))
bezierPath.addCurve(to: CGPoint(x: 30.12, y: 33.83), controlPoint1: CGPoint(x: 30.4, y: 33.57), controlPoint2: CGPoint(x: 30.24, y: 33.75))
bezierPath.addCurve(to: CGPoint(x: 29.67, y: 33.94), controlPoint1: CGPoint(x: 30, y: 33.92), controlPoint2: CGPoint(x: 29.87, y: 33.95))
bezierPath.addCurve(to: CGPoint(x: 28.82, y: 33.66), controlPoint1: CGPoint(x: 29.28, y: 33.91), controlPoint2: CGPoint(x: 28.84, y: 33.67))
bezierPath.addLine(to: CGPoint(x: 16.66, y: 24.63))
bezierPath.addCurve(to: CGPoint(x: 30.24, y: 11.88), controlPoint1: CGPoint(x: 17.86, y: 23.5), controlPoint2: CGPoint(x: 25.86, y: 15.9))
bezierPath.addCurve(to: CGPoint(x: 29.69, y: 10.6), controlPoint1: CGPoint(x: 30.72, y: 11.42), controlPoint2: CGPoint(x: 30.33, y: 10.59))
bezierPath.addCurve(to: CGPoint(x: 27.45, y: 11.72), controlPoint1: CGPoint(x: 28.87, y: 10.76), controlPoint2: CGPoint(x: 28.19, y: 11.35))
bezierPath.addCurve(to: CGPoint(x: 10.91, y: 21.68), controlPoint1: CGPoint(x: 22.04, y: 14.9), controlPoint2: CGPoint(x: 11.81, y: 21.13))
bezierPath.addCurve(to: CGPoint(x: 3.26, y: 19.29), controlPoint1: CGPoint(x: 10.44, y: 21.53), controlPoint2: CGPoint(x: 6.11, y: 20.17))
bezierPath.addCurve(to: CGPoint(x: 2.59, y: 18.92), controlPoint1: CGPoint(x: 2.81, y: 19.11), controlPoint2: CGPoint(x: 2.65, y: 18.97))
bezierPath.addCurve(to: CGPoint(x: 2.59, y: 18.92), controlPoint1: CGPoint(x: 2.59, y: 18.92), controlPoint2: CGPoint(x: 2.59, y: 18.92))
bezierPath.addCurve(to: CGPoint(x: 4.19, y: 18.01), controlPoint1: CGPoint(x: 2.8, y: 18.67), controlPoint2: CGPoint(x: 3.67, y: 18.19))
bezierPath.addCurve(to: CGPoint(x: 9.25, y: 16.06), controlPoint1: CGPoint(x: 4.57, y: 17.87), controlPoint2: CGPoint(x: 6.46, y: 17.14))
bezierPath.addCurve(to: CGPoint(x: 19.3, y: 12.16), controlPoint1: CGPoint(x: 12.05, y: 14.98), controlPoint2: CGPoint(x: 15.67, y: 13.57))
bezierPath.addCurve(to: CGPoint(x: 34.56, y: 6.23), controlPoint1: CGPoint(x: 24.39, y: 10.19), controlPoint2: CGPoint(x: 29.48, y: 8.21))
bezierPath.addCurve(to: CGPoint(x: 35.24, y: 6.07), controlPoint1: CGPoint(x: 34.81, y: 6.11), controlPoint2: CGPoint(x: 35.04, y: 6.06))
bezierPath.addLine(to: CGPoint(x: 35.24, y: 6.07))
bezierPath.close()
bezierPath.move(to: CGPoint(x: 16.15, y: 26.2))
bezierPath.addLine(to: CGPoint(x: 19.19, y: 28.45))
bezierPath.addCurve(to: CGPoint(x: 15.79, y: 31.79), controlPoint1: CGPoint(x: 18.35, y: 29.29), controlPoint2: CGPoint(x: 16.35, y: 31.26))
bezierPath.addLine(to: CGPoint(x: 16.15, y: 26.2))
bezierPath.addLine(to: CGPoint(x: 16.15, y: 26.2))
bezierPath.close()
fillColor.setFill()
bezierPath.fill()
context.restoreGState()
}
@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()!
//// Resize to Target Frame
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 41, height: 40), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 41, y: resizedFrame.height / 40)
//// Bezier Drawing
let bezierPath = UIBezierPath()
bezierPath.move(to: CGPoint(x: 22.36, y: 21.49))
bezierPath.addLine(to: CGPoint(x: 27.52, y: 21.49))
bezierPath.addLine(to: CGPoint(x: 28.29, y: 15.45))
bezierPath.addLine(to: CGPoint(x: 22.36, y: 15.45))
bezierPath.addLine(to: CGPoint(x: 22.36, y: 11.59))
bezierPath.addCurve(to: CGPoint(x: 25.33, y: 8.65), controlPoint1: CGPoint(x: 22.36, y: 9.84), controlPoint2: CGPoint(x: 22.84, y: 8.65))
bezierPath.addLine(to: CGPoint(x: 28.5, y: 8.64))
bezierPath.addLine(to: CGPoint(x: 28.5, y: 3.24))
bezierPath.addCurve(to: CGPoint(x: 23.88, y: 3), controlPoint1: CGPoint(x: 27.95, y: 3.16), controlPoint2: CGPoint(x: 26.07, y: 3))
bezierPath.addCurve(to: CGPoint(x: 16.17, y: 10.99), controlPoint1: CGPoint(x: 19.3, y: 3), controlPoint2: CGPoint(x: 16.17, y: 5.82))
bezierPath.addLine(to: CGPoint(x: 16.17, y: 15.45))
bezierPath.addLine(to: CGPoint(x: 11, y: 15.45))
bezierPath.addLine(to: CGPoint(x: 11, y: 21.49))
bezierPath.addLine(to: CGPoint(x: 16.17, y: 21.49))
bezierPath.addLine(to: CGPoint(x: 16.17, y: 37))
bezierPath.addLine(to: CGPoint(x: 22.36, y: 37))
bezierPath.addLine(to: CGPoint(x: 22.36, y: 21.49))
bezierPath.close()
bezierPath.usesEvenOddFillRule = true
fillColor.setFill()
bezierPath.fill()
context.restoreGState()
}
@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()!
//// Resize to Target Frame
context.saveGState()
let resizedFrame: CGRect = resizing.apply(rect: CGRect(x: 0, y: 0, width: 41, height: 40), target: targetFrame)
context.translateBy(x: resizedFrame.minX, y: resizedFrame.minY)
context.scaleBy(x: resizedFrame.width / 41, y: resizedFrame.height / 40)
//// Bezier 5 Drawing
let bezier5Path = UIBezierPath()
bezier5Path.move(to: CGPoint(x: 34, y: 16.95))
bezier5Path.addCurve(to: CGPoint(x: 23.83, y: 6), controlPoint1: CGPoint(x: 34.05, y: 11.59), controlPoint2: CGPoint(x: 29.49, y: 6.68))
bezier5Path.addCurve(to: CGPoint(x: 23.47, y: 5.94), controlPoint1: CGPoint(x: 23.72, y: 5.98), controlPoint2: CGPoint(x: 23.6, y: 5.96))
bezier5Path.addCurve(to: CGPoint(x: 22.61, y: 5.85), controlPoint1: CGPoint(x: 23.19, y: 5.9), controlPoint2: CGPoint(x: 22.9, y: 5.85))
bezier5Path.addCurve(to: CGPoint(x: 21.06, y: 7.15), controlPoint1: CGPoint(x: 21.45, y: 5.85), controlPoint2: CGPoint(x: 21.14, y: 6.66))
bezier5Path.addCurve(to: CGPoint(x: 21.28, y: 8.33), controlPoint1: CGPoint(x: 20.98, y: 7.62), controlPoint2: CGPoint(x: 21.05, y: 8.02))
bezier5Path.addCurve(to: CGPoint(x: 22.88, y: 9.02), controlPoint1: CGPoint(x: 21.67, y: 8.85), controlPoint2: CGPoint(x: 22.34, y: 8.94))
bezier5Path.addCurve(to: CGPoint(x: 23.32, y: 9.09), controlPoint1: CGPoint(x: 23.04, y: 9.04), controlPoint2: CGPoint(x: 23.19, y: 9.06))
bezier5Path.addCurve(to: CGPoint(x: 30.94, y: 17.04), controlPoint1: CGPoint(x: 28.4, y: 10.22), controlPoint2: CGPoint(x: 30.11, y: 12.01))
bezier5Path.addCurve(to: CGPoint(x: 30.98, y: 17.48), controlPoint1: CGPoint(x: 30.96, y: 17.17), controlPoint2: CGPoint(x: 30.97, y: 17.32))
bezier5Path.addCurve(to: CGPoint(x: 32.44, y: 19.33), controlPoint1: CGPoint(x: 31.02, y: 18.08), controlPoint2: CGPoint(x: 31.1, y: 19.33))
bezier5Path.addLine(to: CGPoint(x: 32.44, y: 19.33))
bezier5Path.addCurve(to: CGPoint(x: 32.8, y: 19.3), controlPoint1: CGPoint(x: 32.55, y: 19.33), controlPoint2: CGPoint(x: 32.68, y: 19.32))
bezier5Path.addCurve(to: CGPoint(x: 34, y: 17.42), controlPoint1: CGPoint(x: 34.05, y: 19.11), controlPoint2: CGPoint(x: 34.02, y: 17.97))
bezier5Path.addCurve(to: CGPoint(x: 34, y: 17.02), controlPoint1: CGPoint(x: 33.99, y: 17.26), controlPoint2: CGPoint(x: 33.99, y: 17.12))
bezier5Path.addCurve(to: CGPoint(x: 34, y: 16.95), controlPoint1: CGPoint(x: 34, y: 17), controlPoint2: CGPoint(x: 34, y: 16.97))
bezier5Path.close()
bezier5Path.move(to: CGPoint(x: 22.28, y: 4.03))
bezier5Path.addCurve(to: CGPoint(x: 22.69, y: 4.07), controlPoint1: CGPoint(x: 22.43, y: 4.04), controlPoint2: CGPoint(x: 22.57, y: 4.05))
bezier5Path.addCurve(to: CGPoint(x: 35.93, y: 17.71), controlPoint1: CGPoint(x: 31.03, y: 5.35), controlPoint2: CGPoint(x: 34.87, y: 9.31))
bezier5Path.addCurve(to: CGPoint(x: 35.95, y: 18.22), controlPoint1: CGPoint(x: 35.95, y: 17.86), controlPoint2: CGPoint(x: 35.95, y: 18.03))
bezier5Path.addCurve(to: CGPoint(x: 37.45, y: 20.27), controlPoint1: CGPoint(x: 35.97, y: 18.87), controlPoint2: CGPoint(x: 35.99, y: 20.24))
bezier5Path.addLine(to: CGPoint(x: 37.5, y: 20.27))
bezier5Path.addCurve(to: CGPoint(x: 38.59, y: 19.85), controlPoint1: CGPoint(x: 37.96, y: 20.27), controlPoint2: CGPoint(x: 38.33, y: 20.13))
bezier5Path.addCurve(to: CGPoint(x: 38.99, y: 18.1), controlPoint1: CGPoint(x: 39.04, y: 19.38), controlPoint2: CGPoint(x: 39.01, y: 18.67))
bezier5Path.addCurve(to: CGPoint(x: 38.98, y: 17.71), controlPoint1: CGPoint(x: 38.98, y: 17.96), controlPoint2: CGPoint(x: 38.98, y: 17.82))
bezier5Path.addCurve(to: CGPoint(x: 23.05, y: 1.02), controlPoint1: CGPoint(x: 39.08, y: 9.11), controlPoint2: CGPoint(x: 31.64, y: 1.31))
bezier5Path.addCurve(to: CGPoint(x: 22.95, y: 1.03), controlPoint1: CGPoint(x: 23.01, y: 1.02), controlPoint2: CGPoint(x: 22.98, y: 1.02))
bezier5Path.addCurve(to: CGPoint(x: 22.84, y: 1.03), controlPoint1: CGPoint(x: 22.93, y: 1.03), controlPoint2: CGPoint(x: 22.9, y: 1.03))
bezier5Path.addCurve(to: CGPoint(x: 22.54, y: 1.02), controlPoint1: CGPoint(x: 22.76, y: 1.03), controlPoint2: CGPoint(x: 22.65, y: 1.03))
bezier5Path.addCurve(to: CGPoint(x: 22.1, y: 1), controlPoint1: CGPoint(x: 22.41, y: 1.01), controlPoint2: CGPoint(x: 22.25, y: 1))
bezier5Path.addCurve(to: CGPoint(x: 20.44, y: 2.55), controlPoint1: CGPoint(x: 20.73, y: 1), controlPoint2: CGPoint(x: 20.47, y: 1.97))
bezier5Path.addCurve(to: CGPoint(x: 22.28, y: 4.03), controlPoint1: CGPoint(x: 20.36, y: 3.89), controlPoint2: CGPoint(x: 21.66, y: 3.99))
bezier5Path.close()
bezier5Path.move(to: CGPoint(x: 35.53, y: 28.58))
bezier5Path.addCurve(to: CGPoint(x: 35, y: 28.17), controlPoint1: CGPoint(x: 35.35, y: 28.44), controlPoint2: CGPoint(x: 35.17, y: 28.3))
bezier5Path.addCurve(to: CGPoint(x: 32.18, y: 26.1), controlPoint1: CGPoint(x: 34.09, y: 27.43), controlPoint2: CGPoint(x: 33.12, y: 26.76))
bezier5Path.addCurve(to: CGPoint(x: 31.6, y: 25.7), controlPoint1: CGPoint(x: 31.98, y: 25.97), controlPoint2: CGPoint(x: 31.79, y: 25.83))
bezier5Path.addCurve(to: CGPoint(x: 28.3, y: 24.44), controlPoint1: CGPoint(x: 30.4, y: 24.85), controlPoint2: CGPoint(x: 29.32, y: 24.44))
bezier5Path.addCurve(to: CGPoint(x: 24.74, y: 26.7), controlPoint1: CGPoint(x: 26.92, y: 24.44), controlPoint2: CGPoint(x: 25.73, y: 25.2))
bezier5Path.addCurve(to: CGPoint(x: 23.12, y: 27.68), controlPoint1: CGPoint(x: 24.3, y: 27.36), controlPoint2: CGPoint(x: 23.77, y: 27.68))
bezier5Path.addCurve(to: CGPoint(x: 21.81, y: 27.36), controlPoint1: CGPoint(x: 22.73, y: 27.68), controlPoint2: CGPoint(x: 22.29, y: 27.57))
bezier5Path.addCurve(to: CGPoint(x: 13.59, y: 19.35), controlPoint1: CGPoint(x: 17.93, y: 25.6), controlPoint2: CGPoint(x: 15.17, y: 22.91))
bezier5Path.addCurve(to: CGPoint(x: 14.42, y: 15.59), controlPoint1: CGPoint(x: 12.82, y: 17.63), controlPoint2: CGPoint(x: 13.07, y: 16.51))
bezier5Path.addCurve(to: CGPoint(x: 16.5, y: 12.26), controlPoint1: CGPoint(x: 15.18, y: 15.07), controlPoint2: CGPoint(x: 16.6, y: 14.11))
bezier5Path.addCurve(to: CGPoint(x: 9.8, y: 3.12), controlPoint1: CGPoint(x: 16.39, y: 10.16), controlPoint2: CGPoint(x: 11.75, y: 3.84))
bezier5Path.addCurve(to: CGPoint(x: 7.22, y: 3.11), controlPoint1: CGPoint(x: 8.97, y: 2.82), controlPoint2: CGPoint(x: 8.11, y: 2.81))
bezier5Path.addCurve(to: CGPoint(x: 2.56, y: 6.95), controlPoint1: CGPoint(x: 4.97, y: 3.87), controlPoint2: CGPoint(x: 3.36, y: 5.19))
bezier5Path.addCurve(to: CGPoint(x: 2.66, y: 12.7), controlPoint1: CGPoint(x: 1.78, y: 8.64), controlPoint2: CGPoint(x: 1.82, y: 10.63))
bezier5Path.addCurve(to: CGPoint(x: 12.84, y: 28.19), controlPoint1: CGPoint(x: 5.09, y: 18.67), controlPoint2: CGPoint(x: 8.52, y: 23.89))
bezier5Path.addCurve(to: CGPoint(x: 28.27, y: 38.44), controlPoint1: CGPoint(x: 17.06, y: 32.4), controlPoint2: CGPoint(x: 22.26, y: 35.85))
bezier5Path.addCurve(to: CGPoint(x: 29.8, y: 38.9), controlPoint1: CGPoint(x: 28.82, y: 38.68), controlPoint2: CGPoint(x: 29.39, y: 38.8))
bezier5Path.addCurve(to: CGPoint(x: 30.15, y: 38.98), controlPoint1: CGPoint(x: 29.94, y: 38.93), controlPoint2: CGPoint(x: 30.07, y: 38.95))
bezier5Path.addCurve(to: CGPoint(x: 30.3, y: 39), controlPoint1: CGPoint(x: 30.2, y: 38.99), controlPoint2: CGPoint(x: 30.25, y: 39))
bezier5Path.addLine(to: CGPoint(x: 30.35, y: 39))
bezier5Path.addLine(to: CGPoint(x: 30.35, y: 39))
bezier5Path.addCurve(to: CGPoint(x: 37.63, y: 33.46), controlPoint1: CGPoint(x: 33.18, y: 39), controlPoint2: CGPoint(x: 36.58, y: 36.41))
bezier5Path.addCurve(to: CGPoint(x: 35.53, y: 28.58), controlPoint1: CGPoint(x: 38.54, y: 30.88), controlPoint2: CGPoint(x: 36.87, y: 29.61))
bezier5Path.close()
bezier5Path.move(to: CGPoint(x: 23.53, y: 10.86))
bezier5Path.addCurve(to: CGPoint(x: 21.68, y: 11.93), controlPoint1: CGPoint(x: 23.05, y: 10.87), controlPoint2: CGPoint(x: 22.04, y: 10.9))
bezier5Path.addCurve(to: CGPoint(x: 21.74, y: 13.16), controlPoint1: CGPoint(x: 21.52, y: 12.41), controlPoint2: CGPoint(x: 21.54, y: 12.82))
bezier5Path.addCurve(to: CGPoint(x: 23.14, y: 13.9), controlPoint1: CGPoint(x: 22.04, y: 13.67), controlPoint2: CGPoint(x: 22.62, y: 13.82))
bezier5Path.addCurve(to: CGPoint(x: 26.21, y: 17.21), controlPoint1: CGPoint(x: 25.04, y: 14.21), controlPoint2: CGPoint(x: 26.02, y: 15.26))
bezier5Path.addCurve(to: CGPoint(x: 27.7, y: 18.75), controlPoint1: CGPoint(x: 26.3, y: 18.12), controlPoint2: CGPoint(x: 26.91, y: 18.75))
bezier5Path.addLine(to: CGPoint(x: 27.7, y: 18.75))
bezier5Path.addCurve(to: CGPoint(x: 27.88, y: 18.74), controlPoint1: CGPoint(x: 27.76, y: 18.75), controlPoint2: CGPoint(x: 27.82, y: 18.75))
bezier5Path.addCurve(to: CGPoint(x: 29.24, y: 16.68), controlPoint1: CGPoint(x: 28.82, y: 18.63), controlPoint2: CGPoint(x: 29.28, y: 17.94))
bezier5Path.addCurve(to: CGPoint(x: 27.4, y: 12.69), controlPoint1: CGPoint(x: 29.25, y: 15.37), controlPoint2: CGPoint(x: 28.57, y: 13.88))
bezier5Path.addCurve(to: CGPoint(x: 23.53, y: 10.86), controlPoint1: CGPoint(x: 26.23, y: 11.5), controlPoint2: CGPoint(x: 24.82, y: 10.83))
bezier5Path.close()
fillColor.setFill()
bezier5Path.fill()
context.restoreGState()
}
@objc(SocialIconStyleKitResizingBehavior)
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.
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,235 @@
// 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 SPCodeDraw {
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 class var gradient: CGGradient { return Cache.gradient }
@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()!
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 bezier2Path = UIBezierPath()
bezier2Path.move(to: CGPoint(x: 49.5, y: 8.22))
bezier2Path.addLine(to: CGPoint(x: 39.17, y: 40.29))
bezier2Path.addLine(to: CGPoint(x: 36.66, y: 40.32))
bezier2Path.addLine(to: CGPoint(x: 6.89, y: 40.68))
bezier2Path.addLine(to: CGPoint(x: 32.78, y: 60.88))
bezier2Path.addLine(to: CGPoint(x: 32.04, y: 63.39))
bezier2Path.addLine(to: CGPoint(x: 23.17, y: 93.18))
bezier2Path.addLine(to: CGPoint(x: 49.5, y: 73.61))
bezier2Path.addLine(to: CGPoint(x: 51.55, y: 75.13))
bezier2Path.addLine(to: CGPoint(x: 75.83, y: 93.19))
bezier2Path.addLine(to: CGPoint(x: 66.22, y: 60.89))
bezier2Path.addLine(to: CGPoint(x: 68.23, y: 59.32))
bezier2Path.addLine(to: CGPoint(x: 92.11, y: 40.68))
bezier2Path.addLine(to: CGPoint(x: 59.83, y: 40.29))
bezier2Path.addLine(to: CGPoint(x: 49.5, y: 8.22))
bezier2Path.close()
bezier2Path.move(to: CGPoint(x: 52.21, y: 5.04))
bezier2Path.addLine(to: CGPoint(x: 62.38, y: 36.61))
bezier2Path.addLine(to: CGPoint(x: 94.17, y: 37))
bezier2Path.addCurve(to: CGPoint(x: 95.84, y: 42.39), controlPoint1: CGPoint(x: 96.9, y: 37.03), controlPoint2: CGPoint(x: 98.04, y: 40.68))
bezier2Path.addLine(to: CGPoint(x: 70.34, y: 62.29))
bezier2Path.addLine(to: CGPoint(x: 79.81, y: 94.1))
bezier2Path.addCurve(to: CGPoint(x: 75.43, y: 97.43), controlPoint1: CGPoint(x: 80.63, y: 96.84), controlPoint2: CGPoint(x: 77.67, y: 99.09))
bezier2Path.addLine(to: CGPoint(x: 49.5, y: 78.16))
bezier2Path.addLine(to: CGPoint(x: 23.57, y: 97.43))
bezier2Path.addCurve(to: CGPoint(x: 19.19, y: 94.1), controlPoint1: CGPoint(x: 21.34, y: 99.09), controlPoint2: CGPoint(x: 18.37, y: 96.83))
bezier2Path.addLine(to: CGPoint(x: 28.66, y: 62.29))
bezier2Path.addLine(to: CGPoint(x: 3.16, y: 42.39))
bezier2Path.addCurve(to: CGPoint(x: 4.83, y: 36.99), controlPoint1: CGPoint(x: 0.96, y: 40.68), controlPoint2: CGPoint(x: 2.1, y: 37.03))
bezier2Path.addLine(to: CGPoint(x: 36.62, y: 36.61))
bezier2Path.addLine(to: CGPoint(x: 46.79, y: 5.04))
bezier2Path.addCurve(to: CGPoint(x: 52.21, y: 5.04), controlPoint1: CGPoint(x: 47.67, y: 2.32), controlPoint2: CGPoint(x: 51.33, y: 2.32))
bezier2Path.close()
color.setFill()
bezier2Path.fill()
context.restoreGState()
}
@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()!
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: 51.78, y: 4.92))
bezierPath.addLine(to: CGPoint(x: 61.96, y: 36.5))
bezierPath.addLine(to: CGPoint(x: 93.75, y: 36.88))
bezierPath.addCurve(to: CGPoint(x: 95.42, y: 42.28), controlPoint1: CGPoint(x: 96.48, y: 36.92), controlPoint2: CGPoint(x: 97.61, y: 40.57))
bezierPath.addLine(to: CGPoint(x: 69.92, y: 62.17))
bezierPath.addLine(to: CGPoint(x: 79.39, y: 93.98))
bezierPath.addCurve(to: CGPoint(x: 75.01, y: 97.32), controlPoint1: CGPoint(x: 80.2, y: 96.72), controlPoint2: CGPoint(x: 77.24, y: 98.98))
bezierPath.addLine(to: CGPoint(x: 49.08, y: 78.04))
bezierPath.addLine(to: CGPoint(x: 23.14, y: 97.32))
bezierPath.addCurve(to: CGPoint(x: 18.76, y: 93.98), controlPoint1: CGPoint(x: 20.91, y: 98.97), controlPoint2: CGPoint(x: 17.95, y: 96.71))
bezierPath.addLine(to: CGPoint(x: 28.24, y: 62.17))
bezierPath.addLine(to: CGPoint(x: 2.73, y: 42.27))
bezierPath.addCurve(to: CGPoint(x: 4.41, y: 36.88), controlPoint1: CGPoint(x: 0.54, y: 40.57), controlPoint2: CGPoint(x: 1.67, y: 36.91))
bezierPath.addLine(to: CGPoint(x: 36.19, y: 36.49))
bezierPath.addLine(to: CGPoint(x: 46.37, y: 4.92))
bezierPath.addCurve(to: CGPoint(x: 51.78, y: 4.92), controlPoint1: CGPoint(x: 47.24, y: 2.2), controlPoint2: CGPoint(x: 50.91, y: 2.2))
bezierPath.close()
color.setFill()
bezierPath.fill()
context.restoreGState()
}
@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()!
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 clip2Path = UIBezierPath()
clip2Path.move(to: CGPoint(x: 38.59, y: 33.54))
clip2Path.addLine(to: CGPoint(x: 38.59, y: 38.16))
clip2Path.addLine(to: CGPoint(x: 24.9, y: 38.16))
clip2Path.addLine(to: CGPoint(x: 24.9, y: 90.08))
clip2Path.addLine(to: CGPoint(x: 75.1, y: 90.08))
clip2Path.addLine(to: CGPoint(x: 75.1, y: 38.16))
clip2Path.addLine(to: CGPoint(x: 61.41, y: 38.16))
clip2Path.addLine(to: CGPoint(x: 61.41, y: 33.54))
clip2Path.addLine(to: CGPoint(x: 80, y: 33.54))
clip2Path.addLine(to: CGPoint(x: 80, y: 95))
clip2Path.addLine(to: CGPoint(x: 20, y: 95))
clip2Path.addLine(to: CGPoint(x: 20, y: 33.54))
clip2Path.addLine(to: CGPoint(x: 38.59, y: 33.54))
clip2Path.close()
clip2Path.move(to: CGPoint(x: 52.27, y: 61.81))
clip2Path.addLine(to: CGPoint(x: 47.73, y: 61.81))
clip2Path.addLine(to: CGPoint(x: 47.73, y: 14.88))
clip2Path.addLine(to: CGPoint(x: 40.08, y: 22.75))
clip2Path.addLine(to: CGPoint(x: 37.14, y: 19.73))
clip2Path.addLine(to: CGPoint(x: 50, y: 6.5))
clip2Path.addLine(to: CGPoint(x: 62.86, y: 19.73))
clip2Path.addLine(to: CGPoint(x: 59.92, y: 22.75))
clip2Path.addLine(to: CGPoint(x: 52.27, y: 14.88))
clip2Path.addLine(to: CGPoint(x: 52.27, y: 61.81))
clip2Path.close()
color.setFill()
clip2Path.fill()
context.restoreGState()
}
@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 /// 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.
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,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 UIKit
struct SPConstraints {
static func setEqualSizeSuperview(for view: UIView) {
if let superView = view.superview {
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.topAnchor.constraint(equalTo: superView.topAnchor),
view.leftAnchor.constraint(equalTo: superView.leftAnchor),
view.rightAnchor.constraint(equalTo: superView.rightAnchor),
view.bottomAnchor.constraint(equalTo: superView.bottomAnchor)
])
}
}
private init() {}
}
@@ -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 Foundation
func delay(_ delay:Double, closure: @escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when) {
closure()
}
}
@@ -0,0 +1,59 @@
// 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
struct SPDevice {
static var iphone: Bool {
return UIDevice.current.userInterfaceIdiom == .phone
}
static var ipad: Bool {
return UIDevice.current.userInterfaceIdiom == .pad
}
struct Orientation {
static var isPortrait: Bool {
var isPortraitOrientation = true
if UIDevice.current.orientation.isValidInterfaceOrientation {
if UIDevice.current.orientation.isPortrait {
isPortraitOrientation = true
} else {
isPortraitOrientation = false
}
} else {
if UIScreen.main.bounds.width < UIScreen.main.bounds.height {
isPortraitOrientation = true
} else {
isPortraitOrientation = false
}
}
return isPortraitOrientation
}
private init() {}
}
private init() {}
}
@@ -0,0 +1,55 @@
// 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
struct SPDownloader {
static func image(link: String, withComplection complection: @escaping (UIImage?) -> ()) {
guard let url = URL(string: link) else {
DispatchQueue.main.async {
complection(nil)
}
return
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else {
DispatchQueue.main.async {
complection(nil)
}
return
}
DispatchQueue.main.async {
complection(image)
}
}.resume()
}
private init() {}
}
@@ -0,0 +1,49 @@
// 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 Array {
func get(count: Int) -> Array {
if (count < self.count) { return Array(self[0..<count]) }
return Array(self)
}
}
extension Array where Element: Equatable {
mutating func removeDuplicates() {
var result = [Element]()
for value in self {
if result.contains(value) == false { result.append(value) }
}
self = result
}
}
extension Array where Element: Hashable {
func after(item: Element) -> Element? {
if let index = self.firstIndex(of: item), index + 1 < self.count { return self[index + 1] }
return nil
}
}
@@ -0,0 +1,51 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan 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 CGRect {
var bottomX: CGFloat {
get { return self.origin.x + self.width }
set { self.origin.x = newValue - self.width }
}
var bottomY: CGFloat {
get { return self.origin.y + self.height }
set { self.origin.y = newValue - self.height }
}
var minSide: CGFloat {
return min(self.width, self.height)
}
mutating func set(width: CGFloat) {
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: self.height)
}
mutating func set(height: CGFloat) {
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: self.width, height: height)
}
mutating func set(width: CGFloat, height: CGFloat) {
self = CGRect.init(x: self.origin.x, y: self.origin.y, width: width, height: height)
}
}
@@ -0,0 +1,37 @@
// 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 CGSize {
func resize(width: CGFloat) -> CGSize {
let relativeSideSize = self.width / self.height
let newHeight = width / relativeSideSize
return CGSize.init(width: width, height: newHeight)
}
func resize(height: CGFloat) -> CGSize {
let relativeSideSize = self.width / self.height
let newWidth = height * relativeSideSize
return CGSize.init(width: newWidth, height: height)
}
}
@@ -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
}
}
@@ -0,0 +1,37 @@
// 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 Strideable {
mutating func setIfMore(when value: Self) {
if self > value {
self = value
}
}
mutating func setIfFewer(when value: Self) {
if self < value {
self = value
}
}
}
@@ -0,0 +1,101 @@
// 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
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))
}
}
mutating func dropFirst(substring: String) {
if self.hasPrefix(substring) {
self = String(dropFirst(substring.count))
}
}
func uppercasedFirstLetter() -> String {
let lowercaseSctring = self.lowercased()
return lowercaseSctring.prefix(1).uppercased() + lowercaseSctring.dropFirst()
}
mutating func uppercaseFirstLetter() {
self = self.uppercasedFirstLetter()
}
func removeAllSpaces() -> String {
return self.components(separatedBy: .whitespaces).joined()
}
mutating func removeAllSpaces() {
self = self.removeAllSpaces()
}
var isEmail: Bool {
let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailTest = NSPredicate(format:"SELF MATCHES %@", emailRegEx)
return emailTest.evaluate(with: self)
}
var isLink: Bool {
if let url = URL(string: self) {
return UIApplication.shared.canOpenURL(url)
}
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])
}
}
}
}
@@ -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
extension UITextField {
@IBInspectable var placeholderColor: UIColor? {
get {
return self.placeholderColor
}
set {
self.attributedPlaceholder = NSAttributedString(string:self.placeholder != nil ? self.placeholder! : "", attributes:[NSAttributedString.Key.foregroundColor: newValue!])
}
}
}
@@ -0,0 +1,108 @@
// 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 UIAlertController {
static var elementsColor: UIColor {
get {
return UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor
}
set {
UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = newValue
}
}
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 let cancelTitle = cancelButtonTitle {
ac.addAction(UIAlertAction.init(
title: cancelTitle,
style: UIAlertAction.Style.cancel,
handler: nil)
)
}
ac.addAction(UIAlertAction.init(
title: buttonTitle,
style: UIAlertAction.Style.default,
handler: { (action) in
complection()
}))
viewController.present(ac, animated: true, completion: nil)
}
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
)
let style = isDestructive ? .destructive : UIAlertAction.Style.default
ac.addAction(UIAlertAction.init(
title: buttonTitle,
style: style,
handler: { (action) in
complection(true)
}))
ac.addAction(UIAlertAction.init(
title: cancelButtonTitle,
style: UIAlertAction.Style.cancel,
handler: { (action) in
complection(false)
}))
viewController.present(ac, animated: true, completion: nil)
}
}
extension UIAlertController {
func addAction(title: String, complection: @escaping ()->()) {
let action = UIAlertAction(title: title, style: .default) { (action) in
complection()
}
self.addAction(action)
}
func addDestructiveAction(title: String, complection: @escaping ()->()) {
let action = UIAlertAction(title: title, style: .destructive) { (action) in
complection()
}
self.addAction(action)
}
func addCancelAction(title: String, complection: @escaping ()->() = {}) {
let action = UIAlertAction(title: title, style: .cancel) { (action) in
complection()
}
self.addAction(action)
}
}
@@ -0,0 +1,136 @@
// 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 UIButton {
typealias UIButtonTargetClosure = () -> ()
private class ClosureWrapper: NSObject {
let closure: UIButtonTargetClosure
init(_ closure: @escaping UIButtonTargetClosure) {
self.closure = closure
}
}
private struct AssociatedKeys {
static var targetClosure = "targetClosure"
}
private var targetClosure: UIButtonTargetClosure? {
get {
guard let closureWrapper = objc_getAssociatedObject(self, &AssociatedKeys.targetClosure) as? ClosureWrapper else { return nil }
return closureWrapper.closure
}
set(newValue) {
guard let newValue = newValue else { return }
objc_setAssociatedObject(self, &AssociatedKeys.targetClosure, ClosureWrapper(newValue), objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
func target(_ action: @escaping UIButtonTargetClosure) {
targetClosure = action
addTarget(self, action: #selector(UIButton.targetAction), for: .touchUpInside)
}
@objc func targetAction() {
guard let targetClosure = targetClosure else { return }
targetClosure()
}
}
extension UIButton {
func setTitle(_ title: String, color: UIColor? = nil) {
self.setTitle(title, for: .normal)
if let color = color {
self.setTitleColor(color)
}
}
func setTitleColor(_ color: UIColor) {
self.setTitleColor(color, for: .normal)
self.setTitleColor(color.withAlphaComponent(0.7), for: .highlighted)
}
func setImage(_ image: UIImage) {
self.setImage(image, for: .normal)
self.imageView?.contentMode = .scaleAspectFit
}
func removeAllTargets() {
self.removeTarget(nil, action: nil, for: .allEvents)
}
func showText(_ text: String, withComplection completion: (() -> Void)! = {}) {
let baseText = self.titleLabel?.text ?? " "
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
self.setTitle(text, for: .normal)
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 0
}, delay: 0.35,
withComplection: {
self.setTitle(baseText, for: .normal)
SPAnimation.animate(0.2, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
completion()
})
})
})
})
}
func setAnimatableText(_ text: String, withComplection completion: (() -> Void)! = {}) {
SPAnimation.animate(0.3, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
self.setTitle(text, for: .normal)
SPAnimation.animate(0.3, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
completion()
})
})
}
func hideContent(completion: (() -> Void)! = {}) {
SPAnimation.animate(0.25, animations: {
self.titleLabel?.alpha = 0
}, withComplection: {
completion()
})
}
func showContent(completion: (() -> Void)! = {}) {
SPAnimation.animate(0.25, animations: {
self.titleLabel?.alpha = 1
}, withComplection: {
completion()
})
}
}
@@ -0,0 +1,56 @@
// 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 UICollectionView {
var currentIndexCellPath: IndexPath? {
let visibleRect = CGRect(origin: self.contentOffset, size: self.bounds.size)
let visiblePoint = CGPoint.init(x: visibleRect.midX, y: visibleRect.midY)
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()
}
}
}
@@ -0,0 +1,68 @@
// 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 UIColor {
convenience init(hex: String) {
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var alpha: CGFloat = 1.0
var hex: String = hex
if hex.hasPrefix("#") {
let index = hex.index(hex.startIndex, offsetBy: 1)
hex = String(hex[index...])
}
let scanner = Scanner(string: hex)
var hexValue: CUnsignedLongLong = 0
if scanner.scanHexInt64(&hexValue) {
switch (hex.count) {
case 3:
red = CGFloat((hexValue & 0xF00) >> 8) / 15.0
green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0
blue = CGFloat(hexValue & 0x00F) / 15.0
case 4:
red = CGFloat((hexValue & 0xF000) >> 12) / 15.0
green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0
blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0
alpha = CGFloat(hexValue & 0x000F) / 15.0
case 6:
red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0
green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0
blue = CGFloat(hexValue & 0x0000FF) / 255.0
case 8:
red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0
green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0
blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0
alpha = CGFloat(hexValue & 0x000000FF) / 255.0
default:
print("SPUIColorExtension - Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8", terminator: "")
}
} else {
print("SPUIColorExtension - Scan hex error")
}
self.init(red:red, green:green, blue:blue, alpha:alpha)
}
}
@@ -0,0 +1,61 @@
// 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 UIFont {
static func system(weight: FontWeight, size: CGFloat) -> UIFont {
return UIFont.systemFont(ofSize: size, weight: self.weight(for: weight))
}
private static func weight(for weight: FontWeight) -> UIFont.Weight {
switch weight {
case .ultraLight:
return UIFont.Weight.ultraLight
case .light:
return UIFont.Weight.light
case .medium:
return UIFont.Weight.medium
case .regular:
return UIFont.Weight.regular
case .bold:
return UIFont.Weight.bold
case .demiBold:
return UIFont.Weight.semibold
case .heavy:
return UIFont.Weight.heavy
default:
return UIFont.Weight.regular
}
}
enum FontWeight {
case regular
case medium
case light
case ultraLight
case heavy
case bold
case demiBold
case none
}
}
@@ -0,0 +1,43 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan 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 UIImage {
func resize(width: CGFloat) -> UIImage {
let scale = width / self.size.width
let newHeight = self.size.height * scale
UIGraphicsBeginImageContext(CGSize(width: width, height: newHeight))
self.draw(in: CGRect(x: 0, y: 0, width: width, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
var alwaysOriginal: UIImage {
return self.withRenderingMode(.alwaysOriginal)
}
var alwaysTemplate: UIImage {
return self.withRenderingMode(.alwaysTemplate)
}
}
@@ -0,0 +1,55 @@
// 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 UIImageView {
func setNative() {
self.layer.borderWidth = 0.5
self.layer.borderColor = SPNativeColors.midGray.cgColor
self.layer.masksToBounds = true
}
func downloadedFrom(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit, withComplection complection: @escaping (UIImage?) -> () = {_ in }) {
DispatchQueue.main.async {
self.contentMode = mode
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { complection(nil); return }
DispatchQueue.main.async() { () -> Void in
self.image = image
complection(image)
}
}.resume()
}
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)
}
}
@@ -0,0 +1,100 @@
// 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 UILabel {
func setShadowOffsetForLetters(blurRadius: CGFloat = 0, widthOffset: CGFloat = 0, heightOffset: CGFloat, opacity: CGFloat) {
self.layer.shadowRadius = blurRadius
self.layer.shadowOffset = CGSize(
width: widthOffset,
height: heightOffset
)
self.layer.shadowOpacity = Float(opacity)
}
func setShadowOffsetFactorForLetters(blurRadius: CGFloat = 0, widthOffsetFactor: CGFloat = 0, heightOffsetFactor: CGFloat, opacity: CGFloat) {
let widthOffset = widthOffsetFactor * self.frame.width
let heightOffset = heightOffsetFactor * self.frame.height
self.setShadowOffsetForLetters(blurRadius: blurRadius, widthOffset: widthOffset, heightOffset: heightOffset, opacity: opacity)
}
func removeShadowForLetters() {
self.setShadowOffsetForLetters(blurRadius: 0, widthOffset: 0, heightOffset: 0, opacity: 0)
}
func setCenterAlignment() {
self.textAlignment = .center
self.baselineAdjustment = .alignCenters
}
func setLettersSpacing(_ value: CGFloat) {
if let textString = text {
let attrs: [NSAttributedString.Key : Any] = [.kern: value]
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
}
}
@@ -0,0 +1,38 @@
// 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 UINavigationController {
static var elementsColor: UIColor {
get {
if UINavigationBar.appearance().tintColor != nil {
return UINavigationBar.appearance().tintColor
} else {
return SPNativeColors.blue
}
}
set {
UINavigationBar.appearance().tintColor = newValue
}
}
}
@@ -0,0 +1,29 @@
// The MIT License (MIT)
// Copyright © 2017 Ivan Vorobei (hello@ivanvorobei.by)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import UIKit
extension UIScrollView {
func stopScrolling() {
self.setContentOffset(self.contentOffset, animated: false)
}
}
@@ -0,0 +1,52 @@
// 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 UITabBarController {
static var elementsColor: UIColor {
get {
if UITabBar.appearance().tintColor != nil {
return UITabBar.appearance().tintColor
} else {
return SPNativeColors.blue
}
}
set {
UINavigationBar.appearance().tintColor = newValue
}
}
func addTabBarItem(title: String, image: UIImage, selectedImage: UIImage? = nil, controller: UIViewController) {
let tabBarItem = UITabBarItem(
title: title,
image: image,
selectedImage: selectedImage ?? image
)
controller.tabBarItem = tabBarItem
if self.viewControllers == nil { self.viewControllers = [controller] }
else { self.viewControllers?.append(controller) }
}
}
@@ -0,0 +1,59 @@
// 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 UITableView {
var isEmpty: Bool {
return self.lastSectionWithRows == nil
}
func isEmpty(section: Int) -> Bool {
return self.numberOfRows(inSection: section) == 0
}
var lastSection: Int {
return self.numberOfSections - 1
}
var lastSectionWithRows: Int? {
if self.numberOfSections == 0 { return nil }
var section = self.numberOfSections - 1
if section < 0 { return nil }
while section >= 0 {
if self.numberOfRows(inSection: section) != 0 { return section }
section -= 1
}
return nil
}
var firstSectionWithRows: Int? {
if self.numberOfSections == 0 { return nil }
var section = 0
if section > self.numberOfSections - 1 { return nil }
while section <= (self.numberOfSections - 1) {
if self.numberOfRows(inSection: section) != 0 { return section }
section += 1
}
return nil
}
}
@@ -0,0 +1,45 @@
// 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 UITableViewCell {
var accessoryView: UIView? {
return subviews.compactMap { $0 as? UIButton }.first
}
var highlightedColor: UIColor? {
get {
return self.backgroundView?.backgroundColor
}
set {
let backgroundView = UIView()
backgroundView.backgroundColor = SPNativeColors.customGray
self.selectedBackgroundView = backgroundView
}
}
func highlight() {
self.setHighlighted(true, animated: false)
self.setHighlighted(false, animated: true)
}
}
@@ -0,0 +1,39 @@
// 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 UITextField {
var isEmptyText: Bool {
get {
if self.text == "" {
return true
}
if self.text == nil {
return true
}
return false
}
}
}
@@ -0,0 +1,179 @@
// 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 Photos
extension UIViewController {
func present(_ viewControllerToPresent: UIViewController, completion: (() -> Swift.Void)? = nil) {
self.present(viewControllerToPresent, animated: true, completion: completion)
}
@objc func dismiss() {
self.dismiss(animated: true, completion: nil)
}
func wrapToNavigationController(statusBar: SPStatusBar = .dark) -> UINavigationController {
let controller = SPStatusBarManagerNavigationController(rootViewController: self)
controller.statusBar = statusBar
return controller
}
}
extension UIViewController {
func dismissKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
@objc func dismissKeyboard() {
view.endEditing(true)
}
}
extension UIViewController {
func save(image: UIImage) {
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)
} else {
print("Saving image error. Not allowed permission")
}
}
func saveVideo(url: String, complection: @escaping (Bool)->()) {
DispatchQueue.global(qos: .utility).async {
let urls = URL(string: url)
let urldata = try? Data(contentsOf: urls!)
if urldata != nil {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let filepath = "\(documentsPath)/tempfile.mp4"
DispatchQueue.main.async {
let urlsave = URL(fileURLWithPath: filepath)
do {
try urldata!.write(to: urlsave, options: Data.WritingOptions.atomic)
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: urlsave)
}, completionHandler: { (status, error) in
try? FileManager.default.removeItem(atPath: filepath)
DispatchQueue.main.async {
complection(error == nil)
}
})
} catch {
try? FileManager.default.removeItem(atPath: filepath)
complection(false)
}
}
}
}
}
@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
if let _ = error {
self.imageSaved(isSuccses: false)
} else {
self.imageSaved(isSuccses: true)
}
}
@objc func imageSaved(isSuccses: Bool) {
fatalError("SPUIViewControllerExtenshion - Need ovveride 'imageSaved' func")
}
}
extension UIViewController {
func setPrefersLargeNavigationTitle(_ title: String, smallScreenToSmallBar: Bool = true) {
self.navigationItem.title = title
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .automatic
self.navigationController?.navigationBar.prefersLargeTitles = true
}
if smallScreenToSmallBar {
if self.view.frame.width < 375 {
self.setNavigationTitle(title, style: .small)
}
}
}
func setNavigationTitle(_ title: String, style: SPNavigationTitleStyle) {
self.navigationItem.title = title
switch style {
case .large:
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.largeTitleDisplayMode = .always
}
case .small:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
case .stork:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
case .noContent:
if #available(iOS 11.0, *) {
self.navigationItem.largeTitleDisplayMode = .never
}
}
}
}
extension UIViewController {
var safeArea: UIEdgeInsets {
if #available(iOS 11.0, *) {
return self.view.safeAreaInsets
} else {
return UIEdgeInsets.zero
}
}
var navigationBarHeight: CGFloat {
return self.navigationController?.navigationBar.frame.height ?? 0
}
static var statusBarHeight: CGFloat {
return UIApplication.shared.statusBarFrame.height
}
}
extension UIViewController {
var navigationTitleColor: UIColor? {
get {
return (self.navigationController?.navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor) ?? nil
}
set {
let textAttributes: [NSAttributedString.Key: Any]? = [NSAttributedString.Key.foregroundColor: newValue ?? UIColor.black]
self.navigationController?.navigationBar.titleTextAttributes = textAttributes
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.largeTitleTextAttributes = textAttributes
}
}
}
}
@@ -0,0 +1,284 @@
// 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 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 {
if let nextResponder = self.next as? UIViewController { return nextResponder }
else if let nextResponder = self.next as? UIView { return nextResponder.viewController }
else { return nil }
}
}
}
extension UIView {
var safeArea: UIEdgeInsets {
if #available(iOS 11.0, *) {
return self.safeAreaInsets
} else{
return UIEdgeInsets.zero
}
}
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 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!) }
var height = bounds.height * heightFactor
if maxHeight != nil { height.setIfMore(when: maxHeight!) }
self.frame = CGRect.init(x: 0, y: 0, width: width, height: height)
if withCentering {
self.center.x = bounds.width / 2
self.center.y = bounds.height / 2
}
}
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.superview!.frame.height))
}
if customHeight != nil {
self.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: self.superview!.frame.width, height: customHeight!))
}
}
func resize(width: CGFloat) {
let relativeFactor = self.frame.width / self.frame.height
if relativeFactor.isNaN { return }
self.frame = CGRect.init(
x: self.frame.origin.x,
y: self.frame.origin.y,
width: width,
height: width * relativeFactor
)
}
func resize(height: CGFloat) {
let relativeFactor = self.frame.width / self.frame.height
if relativeFactor.isNaN { return }
self.frame = CGRect.init(
x: self.frame.origin.x,
y: self.frame.origin.y,
width: height / relativeFactor,
height: height
)
}
func setYCenter() {
self.center.y = (self.superview?.frame.height ?? 0) / 2
}
func setXCenter() {
self.center.x = (self.superview?.frame.width ?? 0) / 2
}
func setToCenter() {
self.center = CGPoint.init(x: ((self.superview?.frame.width) ?? 0) / 2, y: ((self.superview?.frame.height) ?? 0) / 2)
}
}
extension UIView {
func setParalax(amountFactor: CGFloat) {
let amount = self.frame.minSide * amountFactor
self.setParalax(amount: amount)
}
func setParalax(amount: CGFloat) {
self.motionEffects.removeAll()
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
horizontal.minimumRelativeValue = -amount
horizontal.maximumRelativeValue = amount
let vertical = UIInterpolatingMotionEffect(keyPath: "center.y", type: .tiltAlongVerticalAxis)
vertical.minimumRelativeValue = -amount
vertical.maximumRelativeValue = amount
let group = UIMotionEffectGroup()
group.motionEffects = [horizontal, vertical]
self.addMotionEffect(group)
}
}
extension UIView {
func addGrade(alpha: CGFloat, color: UIColor = UIColor.black) -> UIView {
let gradeView = UIView.init()
gradeView.alpha = 0
self.addSubview(gradeView)
SPConstraints.setEqualSizeSuperview(for: gradeView)
gradeView.alpha = alpha
gradeView.backgroundColor = color
return gradeView
}
}
extension UIView {
func setShadow(
xTranslationFactor: CGFloat,
yTranslationFactor: CGFloat,
widthRelativeFactor: CGFloat,
heightRelativeFactor: CGFloat,
blurRadiusFactor: CGFloat,
shadowOpacity: CGFloat,
cornerRadiusFactor: CGFloat = 0
) {
let shadowWidth = self.frame.width * widthRelativeFactor
let shadowHeight = self.frame.height * heightRelativeFactor
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.minSide * cornerRadiusFactor
let shadowPath = UIBezierPath.init(
roundedRect: CGRect.init(x: xTranslation, y: yTranslation, width: shadowWidth, height: shadowHeight),
cornerRadius: cornerRadius
)
let blurRadius = self.frame.minSide * blurRadiusFactor
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize.zero
self.layer.shadowOpacity = Float(shadowOpacity)
self.layer.shadowRadius = blurRadius
self.layer.masksToBounds = false
self.layer.shadowPath = shadowPath.cgPath;
}
func setShadow(
xTranslation: CGFloat,
yTranslation: CGFloat,
widthRelativeFactor: CGFloat,
heightRelativeFactor: CGFloat,
blurRadius: CGFloat,
shadowOpacity: CGFloat,
cornerRadius: CGFloat = 0
) {
let shadowWidth = self.frame.width * widthRelativeFactor
let shadowHeight = self.frame.height * heightRelativeFactor
let shadowPath = UIBezierPath.init(
roundedRect: CGRect.init(x: xTranslation, y: yTranslation, width: shadowWidth, height: shadowHeight),
cornerRadius: cornerRadius
)
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOffset = CGSize.zero
self.layer.shadowOpacity = Float(shadowOpacity)
self.layer.shadowRadius = blurRadius
self.layer.masksToBounds = false
self.layer.shadowPath = shadowPath.cgPath
}
func removeShadow() {
self.layer.shadowColor = nil
self.layer.shadowOffset = CGSize.zero
self.layer.shadowOpacity = 0
self.layer.shadowPath = nil
}
func addShadowOpacityAnimation(to: CGFloat, duration: CFTimeInterval) {
let animation = CABasicAnimation(keyPath:"shadowOpacity")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
animation.fromValue = self.layer.cornerRadius
animation.fromValue = self.layer.shadowOpacity
animation.toValue = to
animation.duration = duration
self.layer.add(animation, forKey: "shadowOpacity")
self.layer.shadowOpacity = Float(to)
}
}
extension UIView {
func addCornerRadiusAnimation(to: CGFloat, duration: CFTimeInterval) {
let animation = CABasicAnimation(keyPath:"cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
animation.fromValue = self.layer.cornerRadius
animation.toValue = to
animation.duration = duration
self.layer.add(animation, forKey: "cornerRadius")
self.layer.cornerRadius = to
}
func show(duration: TimeInterval = 0.3) {
self.isHidden = false
SPAnimation.animate(duration, animations: {
self.alpha = 1
})
}
func hide(duration: TimeInterval = 0.3) {
SPAnimation.animate(duration, animations: {
self.alpha = 0
}, withComplection: {
self.isHidden = true
})
}
func removeAllAnimations() {
self.layer.removeAllAnimations()
}
}
extension UIView {
func roundCorners(_ corners: UIRectCorner, radius: CGFloat) {
let path = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let mask = CAShapeLayer()
mask.path = path.cgPath
self.layer.mask = mask
}
func round() {
self.layer.cornerRadius = self.frame.minSide / 2
}
}
@@ -0,0 +1,36 @@
// 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 UIVisualEffectView {
convenience init(style: UIBlurEffect.Style) {
let effect = UIBlurEffect(style: style)
self.init(effect: effect)
}
convenience init(vibrancy style: UIBlurEffect.Style) {
let effect = UIBlurEffect(style: style)
let vibrancyEffect = UIVibrancyEffect(blurEffect: effect)
self.init(effect: vibrancyEffect)
}
}
@@ -0,0 +1,37 @@
// 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 UserDefaults {
func set(stringArray array: [String], forKey key: String) {
self.set(array, forKey: key)
}
func set(boolArray array: [Bool], forKey key: String) {
self.set(array, forKey: key)
}
func boolArray(forKey defaultName: String) -> [Bool] {
return UserDefaults.standard.array(forKey: defaultName) as? [Bool] ?? []
}
}
@@ -0,0 +1,54 @@
// 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
struct SPLayout {
static func sizeWith(widthFactor: CGFloat, maxWidth: CGFloat?, heightFactor: CGFloat, maxHeight: CGFloat?, relativeSideFactor: CGFloat?, from size: CGSize) -> CGSize {
var widthArea = size.width * widthFactor
var heightArea = size.height * heightFactor
if let maxWidth = maxWidth {
widthArea.setIfMore(when: maxWidth)
}
if let maxHeight = maxHeight {
heightArea.setIfMore(when: maxHeight)
}
var prepareWidth = widthArea
var prepareHeight = heightArea
if let relativeSideFactor = relativeSideFactor {
prepareHeight = widthArea / relativeSideFactor
if prepareHeight > heightArea {
prepareHeight = heightArea
prepareWidth = heightArea * relativeSideFactor
}
}
return CGSize.init(width: prepareWidth, height: prepareHeight)
}
private init() {}
}
@@ -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
import LocalAuthentication
struct SPLocalAuthentication {
static var isEnable: Bool {
let context = LAContext()
var error: NSError?
return context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error)
}
static func request(reason: String, complecton: @escaping (Bool)->()) {
let context = LAContext()
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, error in
DispatchQueue.main.async { complecton(success) }
}
}
private init() {}
}
@@ -0,0 +1,59 @@
// 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 SPLocale: String, CaseIterable {
case ru = "ru"
case en = "en"
static var current: SPLocale {
set {
UserDefaults.standard.set([newValue.languageCode], forKey: "AppleLanguages")
}
get {
let locale = Locale.preferredLanguages[0].components(separatedBy: "-")[0]
switch locale {
case "en":
return .en
case "ru":
return .ru
default:
return .ru
}
}
}
var languageCode: String {
return self.rawValue
}
var describtion: String {
switch self {
case .en:
return "English"
case .ru:
return "Русский"
}
}
}
@@ -0,0 +1,74 @@
// 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
import MessageUI
struct SPMail {
static var canSendEmail: Bool {
return MFMailComposeViewController.canSendMail()
}
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 {
if let url: URL = URL(string: parametrs!) {
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
}
}
}
}
static func dialog(to email: String, subject: String? = nil, body: String? = nil, on viewController: UIViewController) {
let mailVC = MFMailComposeViewController()
mailVC.mailComposeDelegate = SPMailSingltone.sharedInstance
mailVC.setToRecipients([email])
if subject != nil {
mailVC.setSubject(subject!)
}
if body != nil {
mailVC.setMessageBody(body!, isHTML: false)
}
viewController.present(mailVC, animated: true, completion: nil)
}
fileprivate final class SPMailSingltone: NSObject, MFMailComposeViewControllerDelegate {
static let sharedInstance = SPMailSingltone()
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
}
private init() {}
}
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")
}
@@ -0,0 +1,89 @@
// 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 UserNotifications
struct SPLocalNotification {
var identificator: String? = nil
var text: String
var title: String? = nil
var badge: Int = 0
var timeInterval: TimeInterval
var soundEnabled: Bool = true
var category: SPLocalNotificationCategory? = nil
init(after timeInterval: TimeInterval, text: String) {
self.text = text
self.timeInterval = timeInterval
}
func add() {
let identificator = self.identificator ?? "\(self.timeInterval)\(self.text)\(Int.random(min: 0, max: 1000))"
let notification = UNNotificationRequest(
identifier: identificator,
content: self.content,
trigger: self.trigger
)
let center = UNUserNotificationCenter.current()
center.add(notification) { (error) in
if let error = error {
print("SPLocalNotification - \(error)")
}
}
}
private var content: UNMutableNotificationContent {
let content = UNMutableNotificationContent()
content.body = self.text
content.title = self.title ?? ""
content.badge = NSNumber(value: UIApplication.shared.applicationIconBadgeNumber + self.badge)
content.sound = self.soundEnabled ? UNNotificationSound.default : nil
if let category = self.category {
if #available(iOS 12.0, *) {
let notificationCategory = UNNotificationCategory(identifier: category.identifier, actions: category.actions, intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: nil, categorySummaryFormat: category.summary, options: [])
UNUserNotificationCenter.current().setNotificationCategories([notificationCategory])
content.categoryIdentifier = notificationCategory.identifier
}
}
return content
}
private var trigger: UNTimeIntervalNotificationTrigger {
return UNTimeIntervalNotificationTrigger(timeInterval: self.timeInterval, repeats: false)
}
}
struct SPLocalNotificationCategory {
var identifier: String
var summary: String
var actions: [UNNotificationAction] = []
static var countSymbol: String {
return "%u"
}
}
@@ -0,0 +1,100 @@
// 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 String {
static func random(count: Int) -> String {
let strings = [
"В доме кардинала от меня не было тайн; не раз видел я, как он усердно перелистывает старинные книги и жадно роется в пыли фамильных рукописей. Когда я как-то упрекнул его за бесполезные бессонные ночи, после которых он впадал в болезненное уныние, он взглянул на меня с горькой улыбкой и раскрыл передо мною историю города Рима. В этой книге, в двадцатой главе жизнеописания папы Александра Шестого, я прочел следующие строки, навсегда оставшиеся в моей памяти",
"По этому поводу между отцом и сыном завязался спор. Цезарь считал, что достаточно применить одно из тех средств, которые он всегда держал наготове для своих ближайших друзей, а именно: пресловутый ключ, которым то одного, то другого просили отпереть некий шкаф. На ключе был крохотный железный шип – недосмотр слесаря. Каждый, кто трудился над тугим замком, накалывал себе палец и на другой день умирал. Был еще перстень с львиной головой, который Цезарь надевал, когда хотел пожать руку той или иной особе. Лев впивался в кожу этих избранных рук, и через сутки наступала смерть.",
"Стол накрыли в папских виноградниках возле Сан-Пьетро-ин-Винколи, в прелестном уголке, понаслышке знакомом кардиналам. Роспильози, в восторге от своего нового звания и предвкушая пир, явился с самым веселым лицом. Спада, человек осторожный и очень любивший своего племянника, молодого офицера, подававшего блистательные надежды, взял лист бумаги, перо и написал свое завещание. Потом он послал сказать племяннику, чтобы тот ждал его у виноградников; но посланный, по-видимому, не застал того дома.",
"Спада знал, что значит приглашение на обед. С тех пор как христианство – глубоко цивилизующая сила – восторжествовало в Риме, уже не центурион являлся объявить от имени тирана: «Цезарь желает, чтобы ты умер», а любезный легат с улыбкой говорил от имени папы: «Его святейшество желает, чтобы вы с ним отобедали»"
]
return String(strings.random()!.prefix(count))
}
}
extension Int {
static func random() -> Int {
return Int.random(in: 0...Int.max)
}
static func random(min: Int, max: Int) -> Int {
return Int.random(in: min...max)
}
}
extension Double {
static func random() -> Double {
return Double.random(in: 0...Double.greatestFiniteMagnitude)
}
static func random(min: Double, max: Double) -> Double {
return Double.random(in: min...max)
}
}
extension Float {
static func random() -> Float {
return Float.random(in: 0...Float.greatestFiniteMagnitude)
}
static func random(min: Float, max: Float) -> Float {
return Float.random(in: min...max)
}
}
extension CGFloat {
static func random() -> CGFloat {
return CGFloat.random(in: 0...CGFloat.greatestFiniteMagnitude)
}
static func random(min: CGFloat, max: CGFloat) -> CGFloat {
return CGFloat.random(in: min...max)
}
}
extension Collection where Index == Int {
func random() -> Iterator.Element? {
return isEmpty ? nil : self[Int(arc4random_uniform(UInt32(endIndex)))]
}
}
extension MutableCollection where Index == Int {
mutating func shuffleInPlace() {
if count < 2 { return }
for i in startIndex ..< endIndex - 1 {
let j = Int(arc4random_uniform(UInt32(endIndex - i))) + i
guard i != j else { continue }
self.swapAt(i, j)
}
}
}
@@ -0,0 +1,24 @@
// 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
struct SPShadow { private init() {} }

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