├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Example ├── .swiftlint.yml ├── NotTwitter │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── 120@2x.png │ │ │ ├── 152-1.png │ │ │ ├── 152.png │ │ │ ├── 167.png │ │ │ ├── 180.png │ │ │ ├── Contents.json │ │ │ └── peek-square.png │ │ ├── Contents.json │ │ ├── compose.imageset │ │ │ ├── Contents.json │ │ │ └── compose.png │ │ ├── profile-large.imageset │ │ │ ├── Contents.json │ │ │ └── profile.png │ │ ├── profile-small.imageset │ │ │ ├── Contents.json │ │ │ └── profile-small.png │ │ ├── promo.imageset │ │ │ ├── Contents.json │ │ │ └── promo-no-logo.jpg │ │ ├── reply.imageset │ │ │ ├── Contents.json │ │ │ └── reply.png │ │ ├── share.imageset │ │ │ ├── Contents.json │ │ │ └── share.png │ │ └── star.imageset │ │ │ ├── Contents.json │ │ │ └── star.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Cell.swift │ ├── ContentView.swift │ ├── Info.plist │ ├── SwiftUI │ │ ├── ProfileBackgroundModifier.swift │ │ ├── ProfileFollowsView.swift │ │ ├── ProfileHeaderView.swift │ │ ├── ProfileOptionsView.swift │ │ └── ProfileView.swift │ ├── TextView.swift │ ├── Theming.swift │ └── TimelineViewController.swift ├── Peek.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ └── xcuserdata │ │ ├── Shaps.xcuserdatad │ │ └── xcdebugger │ │ │ └── Breakpoints_v2.xcbkptlist │ │ └── apple.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── Peek.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ ├── WorkspaceSettings.xcsettings │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── Peek │ └── Base.lproj │ │ └── LaunchScreen.storyboard ├── Podfile ├── Podfile.lock └── Pods │ ├── Local Podspecs │ └── Peek.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── Peek.xcscheme │ └── Target Support Files │ ├── Peek │ ├── Info.plist │ ├── Peek-Info.plist │ ├── Peek-dummy.m │ ├── Peek-prefix.pch │ ├── Peek-umbrella.h │ ├── Peek.modulemap │ └── Peek.xcconfig │ └── Pods-NotTwitter │ ├── Info.plist │ ├── Pods-NotTwitter-Info.plist │ ├── Pods-NotTwitter-acknowledgements.markdown │ ├── Pods-NotTwitter-acknowledgements.plist │ ├── Pods-NotTwitter-dummy.m │ ├── Pods-NotTwitter-frameworks.sh │ ├── Pods-NotTwitter-resources.sh │ ├── Pods-NotTwitter-umbrella.h │ ├── Pods-NotTwitter.debug.xcconfig │ ├── Pods-NotTwitter.modulemap │ └── Pods-NotTwitter.release.xcconfig ├── LICENSE ├── PaintCode ├── Absolute Layout.pcvd ├── Application.pcvd ├── Attributes.pcvd ├── AutoResize.pcvd ├── Controller.pcvd ├── Device.pcvd ├── Hierarchy.pcvd ├── Layers.pcvd ├── Layout.pcvd ├── Orientation.pcvd ├── RectEdge.pcvd ├── Relative Layout.pcvd ├── Report.pcvd ├── Reports.pcvd ├── Screen.pcvd ├── SingleOrientation.pcvd ├── Toggle Inspectors.pcvd ├── Trash.pcvd └── View.pcvd ├── Peek.podspec ├── Pod └── Classes │ ├── Accessory Views │ ├── BoolAccessoryView.swift │ ├── ColorAccessoryView.swift │ └── PeekAccessoryProviding.swift │ ├── Controllers & Views │ ├── Inspectors │ │ ├── CollapsibleSections.swift │ │ ├── PeekInspectorCell.swift │ │ └── PreviewCell.swift │ ├── Overlays │ │ ├── Layout │ │ │ ├── PeekLayoutOverlayView.swift │ │ │ ├── PeekLayoutView.swift │ │ │ └── PeekMetricView.swift │ │ ├── PeekButton.swift │ │ ├── PeekOverlayView.swift │ │ ├── PeekSelectionView.swift │ │ └── Text │ │ │ ├── LayoutManager.swift │ │ │ └── PeekTextOverlayView.swift │ ├── PeekInspectorViewController.swift │ ├── PeekPresentationController.swift │ ├── PeekSectionedViewController.swift │ ├── PeekViewController.swift │ ├── Reporting │ │ ├── Report.swift │ │ ├── ReportActivity.swift │ │ └── ReportViewController.swift │ └── UIViewController+Modal.swift │ ├── Coordinator │ ├── Attribute.swift │ ├── Coordinator.swift │ └── Group.swift │ ├── GraphicsRenderer │ ├── ImageRenderer.swift │ ├── PDFRenderer.swift │ ├── Platforms.swift │ ├── Renderer.swift │ └── RendererDrawable.swift │ ├── Helpers │ ├── Bool+Toggle.swift │ ├── CGSize+Resize.swift │ ├── Collection+NilOrEmpty.swift │ ├── Color+Extensions.swift │ ├── Color.swift │ ├── Constraints.swift │ ├── Enum+Cases.swift │ ├── Enum+Descriptions.swift │ ├── NSNumber+Extensions.swift │ ├── NSObject+Extensions.swift │ ├── PeekTapGestureRecognizer.swift │ ├── String+Extensions.swift │ ├── UIDevice+Extensions.swift │ ├── UIImage+Resize.swift │ ├── UIView+Extensions.swift │ ├── UIViewController+Extensions.swift │ └── UIWindow+Extensions.swift │ ├── Images │ ├── Images+Inspectors.swift │ ├── Images+Orientation.swift │ └── Images+OrientationMask.swift │ ├── Model │ ├── ContextDataSource.swift │ ├── Peekable.swift │ └── UIView+Filter.swift │ ├── Peek.swift │ ├── PeekOptions.swift │ ├── PeekTheme+Colors.swift │ ├── Peekable │ ├── Bundle+Peekable.swift │ ├── CALayer+Peekable.swift │ ├── NSAttributedString+Peekable.swift │ ├── NSLayoutConstraint+Peekable.swift │ ├── NSString+Peekable.swift │ ├── UIActivityIndicatorView+Peekable.swift │ ├── UIApplication+Peekable.swift │ ├── UIBarButtonItem+Peekable.swift │ ├── UIButton+Peekable.swift │ ├── UIColor+Peekable.swift │ ├── UIControl+Peekable.swift │ ├── UIDevice+Peekable.swift │ ├── UIFont+Peekable.swift │ ├── UIImage+Peekable.swift │ ├── UIImageView+Peekable.swift │ ├── UILabel+Peekable.swift │ ├── UINavigationBar+Peekable.swift │ ├── UIPageControl+Peekable.swift │ ├── UIPicker+Peekable.swift │ ├── UIProgressView+Peekable.swift │ ├── UIScreen+Peekable.swift │ ├── UIScrollView+Peekable.swift │ ├── UISegmentedControl+Peekable.swift │ ├── UISlider+Peekable.swift │ ├── UIStackView+Peekable.swift │ ├── UIStepper+Peekable.swift │ ├── UISwitch+Peekable.swift │ ├── UITextField+Peekable.swift │ ├── UITextView+Peekable.swift │ ├── UIToolbar+Peekable.swift │ ├── UIView+Peekable.swift │ └── UIViewController+Peekable.swift │ ├── Transformers │ ├── NSNumber+Transformer.swift │ ├── NSValue+Transformer.swift │ └── UIColor+Transformer.swift │ └── VolumeController.swift ├── README.md ├── preview.gif ├── preview.jpg └── pull_request_template.md /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Carthage 26 | Carthage -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 5.2.0 4 | 5 | - Swift 4.1 support 6 | - Objective-C support returns 7 | - Minor refactoring/cleanup 8 | - 9 | 10 | 5.1.0 11 | 12 | Note: This will be the last release targetting Swift 4.0 13 | 14 | **[Added]** 15 | 16 | Peek now supports a Light theme 17 | Peek now recommends alternate values for enum types 18 | Peek now provides a Static Library variant via Cocoapods: `pod 'PeekStatic'` 19 | Peek now supports Carthage 20 | 21 | **[Removed]** 22 | 23 | Classes and functions that should never have been public have been removed 24 | 25 | **[Fixed]** 26 | 27 | Fixing branch name in CONTRIBUTING.md and pull_request_template.md 28 | Lots of small refactors and optimizations thanks to @valeriyvan 29 | 30 | 5.0 (Major Release) 31 | - 32 | 33 | - All new design 34 | - Unified Inspectors: 35 | - Collapse/Expand Groups 36 | - Nested Inspectors 37 | - Previews 38 | - Revamped Reporting 39 | - Accessibilty, StackViews & More 40 | - Removed InkKit & SwiftLayout dependencies 41 | - and more 42 | 43 | > Note: Dropped support for iOS 8.x. 44 | 45 | 4.0 46 | - 47 | 48 | Just a Swift compatibility update. 49 | Minor changes as per Swift APIs but no functional changes to Peek. 50 | 51 | 3.0 52 | - 53 | 54 | Swift 3.x unfortunately broke some of the compatilibity issues I required to work with the runtime. 55 | Please use the Swift 4 version instead. 56 | 57 | 2.2.0 58 | - 59 | 60 | * Slack integration 61 | * Email reports 62 | * Screenshot upload block (for Slack) 63 | * NSAttributedString support, including paragraph styles 64 | 65 | 2.1.0 66 | - 67 | 68 | * Enable with options 69 | * Force shake gesture on device 70 | * Allow container selection 71 | 72 | 2.0 (Major Release) 73 | - 74 | 75 | * Absolute layout overlay 76 | * Multiple inspectors 77 | * Swift Support 78 | * iOS 8.3 Support 79 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at shapsuk@me.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # There are a few guidelines to follow when contributing to this project. 2 | 3 | 1. Always use a Pull Request to submit a change 4 | 2. If its a non-trivial change or new feature, always discuss it with the core contributor(s) first via the issues – PRs that don't follow this rule will likely be declined 5 | 3. No new dependencies will be accepted 6 | 4. Extensions support 7 | 5. Always use SwiftLint to ensure coding styles are met 8 | 6. Make sure you pull all recent changes from `develop` and fix any conflicts 9 | 10 | ## Pull Requests 11 | 12 | Pull requests should also point to `develop`. No requests made to `master` will be allowed. 13 | 14 | All changes, no matter how trivial, must be done via pull request. Commits should never be made directly on the master branch. 15 | 16 | ## Feature Requests 17 | 18 | If you have a feature request, please open an Issue about it first. I will reply to all feature requests but its important that it fits within the design goals and feature roadmap of the product. 19 | 20 | So open an issue and we can discuss it, I'm always open to new ideas and suggestions, I just prefer to discuss first. 21 | 22 | ## Dependencies 23 | 24 | One of Peek's design goals is to keep things simple and to have extremely minimal impact on the running app. To that end, I have slowly implemented light-weight versions of libraries that were previously included and managed to strip out all other dependencies. 25 | 26 | > Thus, no new dependencies will be introduced into Peek in the future. 27 | 28 | ## Extensions 29 | 30 | However there are times where a 3rd party component adds value to the project. To satisfy this requirement I am currently working on an Extension based architecture that will make this easier to build where the dependency is inverted. Those projects can depend on Peek instead and still benefit from the best of both worlds 31 | 32 | ## SwiftLint 33 | 34 | The project has already been configured to use SwiftLint, so ensure you have this installed and all issues fixed before submitting a PR 35 | 36 | ## README 37 | 38 | Where applicable, you should ensure the README is also maintained. A typical example is where a new feature is introduced that warrants some explanation, or perhaps an update to the changelog. 39 | -------------------------------------------------------------------------------- /Example/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | excluded: 2 | - Pods 3 | 4 | included: 5 | - ../Pod 6 | 7 | disabled_rules: 8 | - class_delegate_protocol 9 | - redundant_string_enum_value 10 | - type_name 11 | - redundant_optional_initialization 12 | - operator_whitespace 13 | - trailing_whitespace 14 | - line_length 15 | - todo 16 | - nesting 17 | - cyclomatic_complexity 18 | - function_body_length 19 | - identifier_name 20 | - trailing_comma 21 | 22 | file_length: 23 | warning: 500 24 | error: 700 25 | 26 | large_tuple: 27 | warning: 10 28 | 29 | force_cast: 30 | warning 31 | 32 | force_try: 33 | warning 34 | 35 | function_parameter_count: 36 | warning: 6 37 | -------------------------------------------------------------------------------- /Example/NotTwitter/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // NotTwitter 4 | // 5 | // Created by Shaps Benkau on 10/03/2018. 6 | // Copyright © 2018 Snippex. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Peek 11 | 12 | @UIApplicationMain 13 | final class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { 18 | Theme.apply() 19 | 20 | window?.peek.enableWithOptions { options in 21 | options.theme = .dark 22 | options.activationMode = .auto 23 | options.ignoresContainerViews = false 24 | 25 | /* 26 | Configure the metadata asscociated with this app. 27 | */ 28 | options.metadata = [ 29 | "Environment": "UAT" 30 | ] 31 | } 32 | 33 | return true 34 | } 35 | 36 | override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { 37 | // iOS 8/9 requires device motion handlers to be on the AppDelegate 38 | window?.peek.handleShake(motion) 39 | } 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/120@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/120@2x.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/152-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/152-1.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/152.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "120@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "180.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "size" : "20x20", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "20x20", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "29x29", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "29x29", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "40x40", 68 | "scale" : "1x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "40x40", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "76x76", 77 | "idiom" : "ipad", 78 | "filename" : "152-1.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "76x76", 83 | "idiom" : "ipad", 84 | "filename" : "152.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "83.5x83.5", 89 | "idiom" : "ipad", 90 | "filename" : "167.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "1024x1024", 95 | "idiom" : "ios-marketing", 96 | "filename" : "peek-square.png", 97 | "scale" : "1x" 98 | } 99 | ], 100 | "info" : { 101 | "version" : 1, 102 | "author" : "xcode" 103 | }, 104 | "properties" : { 105 | "pre-rendered" : true 106 | } 107 | } -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/peek-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/AppIcon.appiconset/peek-square.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/compose.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "compose.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/compose.imageset/compose.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/compose.imageset/compose.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/profile-large.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "profile.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/profile-large.imageset/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/profile-large.imageset/profile.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/profile-small.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "profile-small.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "original" 23 | } 24 | } -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/profile-small.imageset/profile-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/profile-small.imageset/profile-small.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/promo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "promo-no-logo.jpg", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | }, 21 | "properties" : { 22 | "template-rendering-intent" : "original" 23 | } 24 | } -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/promo.imageset/promo-no-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/promo.imageset/promo-no-logo.jpg -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/reply.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "reply.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/reply.imageset/reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/reply.imageset/reply.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/share.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "share.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/share.imageset/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/share.imageset/share.png -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/star.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "idiom" : "universal", 13 | "filename" : "star.png", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/NotTwitter/Assets.xcassets/star.imageset/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/Example/NotTwitter/Assets.xcassets/star.imageset/star.png -------------------------------------------------------------------------------- /Example/NotTwitter/Cell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Cell.swift 3 | // NotTwitter 4 | // 5 | // Created by Shaps Benkau on 10/03/2018. 6 | // Copyright © 2018 Snippex. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public final class Cell: UITableViewCell { 12 | 13 | @IBOutlet weak var profileImageView: UIImageView! 14 | @IBOutlet weak var usernameLabel: UILabel! 15 | @IBOutlet weak var messageLabel: TextView! 16 | @IBOutlet weak var embedImageView: UIImageView! 17 | @IBOutlet weak var replyButton: UIButton! 18 | @IBOutlet weak var favouriteButton: UIButton! 19 | @IBOutlet weak var shareButton: UIButton! 20 | 21 | public override func awakeFromNib() { 22 | super.awakeFromNib() 23 | 24 | replyButton.titleLabel?.font = UIFont.preferredFont(forTextStyle: .footnote) 25 | favouriteButton.titleLabel?.font = UIFont.preferredFont(forTextStyle: .footnote) 26 | shareButton.titleLabel?.font = UIFont.preferredFont(forTextStyle: .footnote) 27 | 28 | if #available(iOS 10.0, *) { 29 | usernameLabel.adjustsFontForContentSizeCategory = true 30 | messageLabel.adjustsFontForContentSizeCategory = true 31 | replyButton.titleLabel?.adjustsFontForContentSizeCategory = true 32 | favouriteButton.titleLabel?.adjustsFontForContentSizeCategory = true 33 | shareButton.titleLabel?.adjustsFontForContentSizeCategory = true 34 | } 35 | 36 | if #available(iOS 11.0, *) { 37 | replyButton.adjustsImageSizeForAccessibilityContentSizeCategory = true 38 | favouriteButton.adjustsImageSizeForAccessibilityContentSizeCategory = true 39 | shareButton.adjustsImageSizeForAccessibilityContentSizeCategory = true 40 | } 41 | 42 | selectedBackgroundView = UIView() 43 | selectedBackgroundView?.backgroundColor = .selection 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Example/NotTwitter/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @available(iOS 13.0, *) 4 | struct ContentView: View { 5 | @State var a: String = "AAAAA" 6 | @State var b: String = "BBBB" 7 | @State var c: String = "CCCCCC" 8 | 9 | var body: some View { 10 | VStack { 11 | ZStack(alignment: .mid) { 12 | // create vertical and horizontal 13 | // space to align to 14 | HStack { Spacer() } 15 | VStack { Spacer() } 16 | 17 | VStack(alignment: .midX) { 18 | Text(self.a) 19 | 20 | HStack(alignment: .center) { 21 | Text(self.c) 22 | 23 | 24 | Text(self.b) 25 | .font(.title) 26 | .border(Color.blue) 27 | .alignmentGuide(.midX) { d in 28 | (d[.leading] + d[.trailing])/2 29 | } 30 | .alignmentGuide(.midY) { d in 31 | (d[.top] + d[.bottom])/2 32 | } 33 | } 34 | } 35 | } 36 | .layoutPriority(1.0) 37 | 38 | TextField("", text: self.$b).textFieldStyle(RoundedBorderTextFieldStyle()) 39 | } 40 | } 41 | } 42 | 43 | @available(iOS 13.0, *) 44 | fileprivate extension HorizontalAlignment { 45 | @available(iOS 13.0, *) 46 | enum MidX : AlignmentID { 47 | static func defaultValue(in d: ViewDimensions) -> CGFloat { 48 | return (d[.leading] + d[.trailing])/2 49 | } 50 | } 51 | 52 | static let midX = HorizontalAlignment(MidX.self) 53 | } 54 | 55 | @available(iOS 13.0, *) 56 | fileprivate extension VerticalAlignment { 57 | @available(iOS 13.0, *) 58 | enum MidY : AlignmentID { 59 | static func defaultValue(in d: ViewDimensions) -> CGFloat { 60 | return (d[.top] + d[.bottom])/2 61 | } 62 | } 63 | 64 | static let midY = VerticalAlignment(MidY.self) 65 | } 66 | 67 | @available(iOS 13.0, *) 68 | fileprivate extension Alignment { 69 | @available(iOS 13.0, *) 70 | static let mid = Alignment(horizontal: .midX, vertical: .midY) 71 | } 72 | 73 | @available(iOS 13.0, *) 74 | struct ContentView_Previews: PreviewProvider { 75 | static var previews: some View { 76 | ContentView() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Example/NotTwitter/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Not Twitter 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example/NotTwitter/SwiftUI/ProfileBackgroundModifier.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @available(iOS 13, *) 4 | struct ProfileBackgroundModifier: ViewModifier { 5 | func body(content: Content) -> some View { 6 | content 7 | .background( 8 | RoundedRectangle(cornerRadius: 10, style: .continuous) 9 | .foregroundColor(Color(.systemGray5)) 10 | ) 11 | } 12 | } 13 | 14 | @available(iOS 13, *) 15 | struct ProfileBackgroundModifier_Previews: PreviewProvider { 16 | static var previews: some View { 17 | HStack { 18 | Spacer() 19 | /*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/ 20 | Spacer() 21 | } 22 | .modifier(ProfileBackgroundModifier()) 23 | .previewLayout(.sizeThatFits) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Example/NotTwitter/SwiftUI/ProfileFollowsView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @available(iOS 13, *) 4 | struct TweetFollowersView: View { 5 | 6 | var body: some View { 7 | VStack { 8 | HStack { 9 | Spacer() 10 | VStack { 11 | Text("FOLLOWERS") 12 | .font(.callout) 13 | .foregroundColor(.secondary) 14 | Text("4,559") 15 | .font(.title) 16 | } 17 | 18 | Spacer() 19 | Divider() 20 | Spacer() 21 | 22 | VStack { 23 | Text("FOLLOWING") 24 | .font(.callout) 25 | .foregroundColor(.secondary) 26 | Text("256") 27 | .font(.title) 28 | } 29 | 30 | Spacer() 31 | } 32 | } 33 | .padding(.vertical) 34 | } 35 | 36 | } 37 | 38 | @available(iOS 13, *) 39 | struct ProfileFollowsView_Previews: PreviewProvider { 40 | static var previews: some View { 41 | TweetFollowersView() 42 | .fixedSize(horizontal: false, vertical: true) 43 | .previewLayout(.sizeThatFits) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Example/NotTwitter/SwiftUI/ProfileHeaderView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @available(iOS 13, *) 4 | struct ProfileHeaderView: View { 5 | 6 | var body: some View { 7 | ZStack(alignment: .bottomLeading) { 8 | HStack { 9 | Image("promo") 10 | .resizable() 11 | .scaledToFit() 12 | .aspectRatio(1, contentMode: .fill) 13 | } 14 | 15 | HStack { 16 | Image("profile-large") 17 | .overlay( 18 | Circle() 19 | .stroke(lineWidth: 2) 20 | .foregroundColor(Color(.systemGroupedBackground)) 21 | ) 22 | 23 | VStack(alignment: .leading, spacing: 5) { 24 | Text("Shaps Benkau") 25 | .font(.headline) 26 | 27 | Text("@shaps") 28 | .foregroundColor(.secondary) 29 | } 30 | .alignmentGuide(VerticalAlignment.center) { 31 | d in d[.top] - 10 32 | } 33 | } 34 | .alignmentGuide(.bottom) { 35 | d in d[VerticalAlignment.center] 36 | } 37 | .padding(.horizontal) 38 | } 39 | .clipped() 40 | } 41 | 42 | } 43 | 44 | @available(iOS 13, *) 45 | struct ProfileHeaderView_Previews: PreviewProvider { 46 | static var previews: some View { 47 | ProfileHeaderView() 48 | .previewLayout(.fixed(width: 320, height: 300)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Example/NotTwitter/SwiftUI/ProfileOptionsView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @available(iOS 13, *) 4 | struct ProfileOptionsView: View { 5 | 6 | var body: some View { 7 | VStack(spacing: 0) { 8 | HStack { 9 | Text("Tweets") 10 | Spacer() 11 | Image(systemName: "chevron.right") 12 | .foregroundColor(.secondary) 13 | } 14 | .padding() 15 | 16 | Divider() 17 | .padding(.leading) 18 | 19 | HStack(spacing: 20) { 20 | Text("Lists") 21 | Spacer() 22 | Image(systemName: "chevron.right") 23 | .foregroundColor(.secondary) 24 | } 25 | .padding() 26 | } 27 | } 28 | 29 | } 30 | 31 | @available(iOS 13, *) 32 | struct ProfileOptionsView_Previews: PreviewProvider { 33 | static var previews: some View { 34 | ProfileOptionsView() 35 | .previewLayout(.sizeThatFits) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Example/NotTwitter/SwiftUI/ProfileView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | @available(iOS 13, *) 4 | struct ProfileView: View { 5 | 6 | var body: some View { 7 | ScrollView { 8 | ProfileHeaderView() 9 | 10 | VStack(spacing: 20) { 11 | TweetFollowersView() 12 | .modifier(ProfileBackgroundModifier()) 13 | 14 | ProfileOptionsView() 15 | .modifier(ProfileBackgroundModifier()) 16 | } 17 | .padding() 18 | } 19 | .background( 20 | Color(.systemGroupedBackground) 21 | .edgesIgnoringSafeArea(.all) 22 | ) 23 | .navigationBarTitle("Shaps", displayMode: .inline) 24 | } 25 | 26 | } 27 | 28 | @available(iOS 13, *) 29 | struct ProfileView_Previews: PreviewProvider { 30 | static var previews: some View { 31 | ProfileView() 32 | .edgesIgnoringSafeArea(.top) 33 | .preferredColorScheme(.dark) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Example/NotTwitter/TextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TextView.swift 3 | // NotTwitter 4 | // 5 | // Created by Shaps Benkau on 10/03/2018. 6 | // Copyright © 2018 Snippex. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public final class TextView: UITextView { 12 | 13 | public override func awakeFromNib() { 14 | super.awakeFromNib() 15 | 16 | textContainerInset = .zero 17 | textContainer.lineFragmentPadding = 0 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Example/NotTwitter/Theming.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theming.swift 3 | // NotTwitter 4 | // 5 | // Created by Shaps Benkau on 10/03/2018. 6 | // Copyright © 2018 Snippex. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | 13 | public static var separator: UIColor { 14 | return UIColor(red: 189/255, green: 198/255, blue: 205/255, alpha: 1) 15 | } 16 | 17 | public static var background: UIColor { 18 | return .white 19 | } 20 | 21 | public static var selection: UIColor { 22 | return UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1) 23 | } 24 | 25 | } 26 | 27 | public struct Theme { 28 | 29 | public static func apply() { 30 | let bar = UINavigationBar.appearance() 31 | 32 | bar.barTintColor = .background 33 | bar.titleTextAttributes = [ 34 | .foregroundColor: UIColor(red: 21/255, green: 23/255, blue: 26/255, alpha: 1), 35 | .font: UIFont.systemFont(ofSize: 16, weight: .black) 36 | ] 37 | 38 | let table = UITableView.appearance() 39 | 40 | table.backgroundColor = .background 41 | table.separatorColor = .separator 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Example/NotTwitter/TimelineViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimelineViewController.swift 3 | // NotTwitter 4 | // 5 | // Created by Shaps Benkau on 10/03/2018. 6 | // Copyright © 2018 Snippex. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftUI 11 | import Peek 12 | 13 | public final class TimelineViewController: UITableViewController { 14 | 15 | public override func viewDidLoad() { 16 | super.viewDidLoad() 17 | 18 | navigationItem.rightBarButtonItem?.accessibilityHint = "New Message" 19 | navigationItem.rightBarButtonItem?.accessibilityIdentifier = "navigation.newMessage" 20 | } 21 | 22 | public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 23 | return 5 24 | } 25 | 26 | public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 27 | let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Cell 28 | return cell 29 | } 30 | 31 | public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 32 | tableView.deselectRow(at: indexPath, animated: true) 33 | 34 | if #available(iOS 13, *) { 35 | let controller = HostingController(rootView: ProfileView()) 36 | navigationController?.pushViewController(controller, animated: true) 37 | } 38 | } 39 | 40 | } 41 | 42 | extension TimelineViewController { 43 | 44 | @IBAction private func newMessage() { print("New message") } 45 | @IBAction private func toggleFavourite() { print("Favourite toggled") } 46 | @IBAction private func reshare() { print("Re-shared") } 47 | 48 | } 49 | 50 | // MARK: Peek 51 | extension TimelineViewController { 52 | 53 | public override var canBecomeFirstResponder: Bool { 54 | return true 55 | } 56 | 57 | public override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { 58 | // iOS 10 now requires device motion handlers to be on a UIViewController 59 | view.window?.peek.handleShake(motion) 60 | } 61 | 62 | } 63 | 64 | @available(iOS 13, *) 65 | final class HostingController: UIHostingController where Content: View { 66 | 67 | public override var canBecomeFirstResponder: Bool { 68 | return true 69 | } 70 | 71 | public override func motionBegan(_ motion: UIEvent.EventSubtype, with event: UIEvent?) { 72 | // iOS 10 now requires device motion handlers to be on a UIViewController 73 | view.window?.peek.handleShake(motion) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Example/Peek.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Peek.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Example/Peek.xcodeproj/xcuserdata/Shaps.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Example/Peek.xcodeproj/xcuserdata/apple.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Peek-Example.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 607FACCF1AFB9204008FA782 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Peek.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Peek.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Peek.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Peek.xcworkspace/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | use_frameworks! 3 | 4 | target 'NotTwitter' do 5 | platform :ios, '9.0' 6 | pod 'Peek', :path => '../', :configurations => ['Debug'] 7 | end 8 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Peek (5.3.0) 3 | 4 | DEPENDENCIES: 5 | - Peek (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | Peek: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | Peek: 4209f7aa72d00244616f6b4804739c04cae2e457 13 | 14 | PODFILE CHECKSUM: c29c492fe40889a0ba9734d1c078df79ffe7b766 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/Peek.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Peek", 3 | "version": "5.3.0", 4 | "swift_versions": [ 5 | "5.1" 6 | ], 7 | "summary": "All new design. Inspect your iOS application at runtime.", 8 | "homepage": "https://152percent.com/peek", 9 | "screenshots": "https://images.squarespace-cdn.com/content/v1/58d1c3c1b3db2b27db51464f/1521730411276-S3UCD05HTDG34PWBGM4N/ke17ZwdGBToddI8pDm48kEWP3-hvREnweJ050wvhyvB7gQa3H78H3Y0txjaiv_0fDoOvxcdMmMKkDsyUqMSsMWxHk725yiiHCCLfrh8O1z5QPOohDIaIeljMHgDF5CVlOqpeNLcJ80NK65_fV7S1UdrULnJwJtUwRUNy9fIMUJZw9x8WqpJ4rfPF_qYxQ1vxK19DM50qGfsFZg32uC5Iyw/Peek+Promo+%E2%80%93+No+Logo.jpg?format=2500w", 10 | "license": "MIT", 11 | "authors": { 12 | "Shaps Benkau": "shapsuk@me.com" 13 | }, 14 | "source": { 15 | "git": "https://github.com/shaps80/Peek.git", 16 | "tag": "5.3.0" 17 | }, 18 | "social_media_url": "https://twitter.com/shaps", 19 | "platforms": { 20 | "ios": "9.0" 21 | }, 22 | "requires_arc": true, 23 | "source_files": "Pod/Classes/**/*.{swift,h,m}", 24 | "swift_version": "5.1" 25 | } 26 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Peek (5.3.0) 3 | 4 | DEPENDENCIES: 5 | - Peek (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | Peek: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | Peek: 4209f7aa72d00244616f6b4804739c04cae2e457 13 | 14 | PODFILE CHECKSUM: c29c492fe40889a0ba9734d1c078df79ffe7b766 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Peek.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Peek/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 5.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Peek/Peek-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 5.3.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Peek/Peek-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Peek : NSObject 3 | @end 4 | @implementation PodsDummy_Peek 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Peek/Peek-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Peek/Peek-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double PeekVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char PeekVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Peek/Peek.modulemap: -------------------------------------------------------------------------------- 1 | framework module Peek { 2 | umbrella header "Peek-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Peek/Peek.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Peek 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-NotTwitter/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-NotTwitter/Pods-NotTwitter-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-NotTwitter/Pods-NotTwitter-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Peek 5 | 6 | Copyright (c) 2016 Shaps Benkau 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | Generated by CocoaPods - https://cocoapods.org 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-NotTwitter/Pods-NotTwitter-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2016 Shaps Benkau <shapsuk@me.com> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | Peek 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - https://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-NotTwitter/Pods-NotTwitter-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_NotTwitter : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_NotTwitter 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-NotTwitter/Pods-NotTwitter-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_NotTwitterVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_NotTwitterVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-NotTwitter/Pods-NotTwitter.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Peek" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Peek/Peek.framework/Headers" 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | OTHER_LDFLAGS = $(inherited) -framework "Peek" 7 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-NotTwitter/Pods-NotTwitter.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_NotTwitter { 2 | umbrella header "Pods-NotTwitter-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-NotTwitter/Pods-NotTwitter.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS 3 | PODS_BUILD_DIR = ${BUILD_DIR} 4 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 6 | PODS_ROOT = ${SRCROOT}/Pods 7 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Shaps Benkau 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /PaintCode/Absolute Layout.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Absolute Layout.pcvd -------------------------------------------------------------------------------- /PaintCode/Application.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Application.pcvd -------------------------------------------------------------------------------- /PaintCode/Attributes.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Attributes.pcvd -------------------------------------------------------------------------------- /PaintCode/AutoResize.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/AutoResize.pcvd -------------------------------------------------------------------------------- /PaintCode/Controller.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Controller.pcvd -------------------------------------------------------------------------------- /PaintCode/Device.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Device.pcvd -------------------------------------------------------------------------------- /PaintCode/Hierarchy.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Hierarchy.pcvd -------------------------------------------------------------------------------- /PaintCode/Layers.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Layers.pcvd -------------------------------------------------------------------------------- /PaintCode/Layout.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Layout.pcvd -------------------------------------------------------------------------------- /PaintCode/Orientation.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Orientation.pcvd -------------------------------------------------------------------------------- /PaintCode/RectEdge.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/RectEdge.pcvd -------------------------------------------------------------------------------- /PaintCode/Relative Layout.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Relative Layout.pcvd -------------------------------------------------------------------------------- /PaintCode/Report.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Report.pcvd -------------------------------------------------------------------------------- /PaintCode/Reports.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Reports.pcvd -------------------------------------------------------------------------------- /PaintCode/Screen.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Screen.pcvd -------------------------------------------------------------------------------- /PaintCode/SingleOrientation.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/SingleOrientation.pcvd -------------------------------------------------------------------------------- /PaintCode/Toggle Inspectors.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Toggle Inspectors.pcvd -------------------------------------------------------------------------------- /PaintCode/Trash.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/Trash.pcvd -------------------------------------------------------------------------------- /PaintCode/View.pcvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/PaintCode/View.pcvd -------------------------------------------------------------------------------- /Peek.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Peek" 3 | s.version = "5.3.0" 4 | s.swift_versions = ['5.1'] 5 | s.summary = "All new design. Inspect your iOS application at runtime." 6 | s.homepage = "https://152percent.com/peek" 7 | s.screenshots = "https://images.squarespace-cdn.com/content/v1/58d1c3c1b3db2b27db51464f/1521730411276-S3UCD05HTDG34PWBGM4N/ke17ZwdGBToddI8pDm48kEWP3-hvREnweJ050wvhyvB7gQa3H78H3Y0txjaiv_0fDoOvxcdMmMKkDsyUqMSsMWxHk725yiiHCCLfrh8O1z5QPOohDIaIeljMHgDF5CVlOqpeNLcJ80NK65_fV7S1UdrULnJwJtUwRUNy9fIMUJZw9x8WqpJ4rfPF_qYxQ1vxK19DM50qGfsFZg32uC5Iyw/Peek+Promo+%E2%80%93+No+Logo.jpg?format=2500w" 8 | s.license = 'MIT' 9 | s.author = { "Shaps Benkau" => "shapsuk@me.com" } 10 | s.source = { :git => "https://github.com/shaps80/Peek.git", :tag => s.version.to_s } 11 | s.social_media_url = 'https://twitter.com/shaps' 12 | s.platform = :ios, '9.0' 13 | s.requires_arc = true 14 | s.source_files = 'Pod/Classes/**/*.{swift,h,m}' 15 | end 16 | -------------------------------------------------------------------------------- /Pod/Classes/Accessory Views/BoolAccessoryView.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | /// This accessory view is used in Peek to show a 'switch' representing the underlying Bool value 26 | final class BoolAccessoryView: UIView, PeekAccessoryProviding { 27 | 28 | internal var theme: PeekTheme 29 | fileprivate let size = CGSize(width: 30, height: 20) 30 | fileprivate let value: Bool 31 | 32 | init(value: Bool, theme: PeekTheme = .dark) { 33 | self.theme = theme 34 | self.value = value 35 | 36 | super.init(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height)) 37 | 38 | self.backgroundColor = UIColor.clear 39 | 40 | if #available(iOS 11.0, *) { 41 | self.accessibilityIgnoresInvertColors = true 42 | } 43 | } 44 | 45 | required init?(coder aDecoder: NSCoder) { 46 | fatalError("init(coder:) has not been implemented") 47 | } 48 | 49 | override func draw(_ rect: CGRect) { 50 | super.draw(rect) 51 | 52 | let rect = rect.insetBy(dx: 1, dy: 1) 53 | 54 | var bgColor: UIColor? 55 | let bgPath = UIBezierPath(roundedRect: rect, cornerRadius: rect.height / 2) 56 | 57 | var fgColor: UIColor? 58 | var fgPath: UIBezierPath 59 | 60 | if value { 61 | bgColor = UIColor(white: 1, alpha: 0.2) 62 | fgColor = theme.tintColor 63 | fgPath = UIBezierPath(ovalIn: CGRect(x: rect.maxX - rect.height, y: rect.minY, width: rect.height, height: rect.height)) 64 | } else { 65 | bgColor = UIColor(white: 1, alpha: 0.2) 66 | fgColor = theme.secondaryTextColor 67 | fgPath = UIBezierPath(ovalIn: CGRect(x: rect.minX, y: rect.minY, width: rect.height, height: rect.height)) 68 | } 69 | 70 | bgColor?.setFill() 71 | bgPath.fill() 72 | 73 | fgColor?.setFill() 74 | fgPath.fill() 75 | } 76 | 77 | override var intrinsicContentSize: CGSize { 78 | return CGSize(width: size.width, height: size.height) 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Pod/Classes/Accessory Views/ColorAccessoryView.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | /// This accessory view is used in Peek to show an icon representing the underlying UIColor value 26 | final class ColorAccessoryView: UIView, PeekAccessoryProviding { 27 | 28 | public var theme: PeekTheme = .dark 29 | fileprivate let color: UIColor? 30 | fileprivate let size = CGSize(width: 20, height: 20) 31 | private let margin: CGFloat = 8 32 | 33 | init(color: UIColor?) { 34 | self.color = color 35 | super.init(frame: CGRect(x: 0, y: 0, width: size.width + margin, height: size.height)) 36 | backgroundColor = .clear 37 | 38 | if #available(iOS 11.0, *) { 39 | self.accessibilityIgnoresInvertColors = true 40 | } 41 | } 42 | 43 | required init?(coder aDecoder: NSCoder) { 44 | fatalError("init(coder:) has not been implemented") 45 | } 46 | 47 | override func draw(_ rect: CGRect) { 48 | super.draw(rect) 49 | 50 | let rect = CGRect(x: 8, y: 0, width: size.width, height: size.height).insetBy(dx: 1, dy: 1) 51 | let path = UIBezierPath(roundedRect: rect, cornerRadius: size.height / 2) 52 | 53 | color?.setFill() 54 | 55 | UIColor(white: 1, alpha: 0.1).setStroke() 56 | path.lineWidth = 1 57 | path.fill() 58 | path.stroke() 59 | } 60 | 61 | override var intrinsicContentSize: CGSize { 62 | return CGSize(width: size.width + margin, height: size.height) 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Pod/Classes/Accessory Views/PeekAccessoryProviding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeekAccessoryProviding.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 28/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol PeekAccessoryProviding { 11 | var theme: PeekTheme { get set } 12 | var intrinsicContentSize: CGSize { get } 13 | } 14 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Inspectors/CollapsibleSections.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sections.swift 3 | // GraphicsRenderer 4 | // 5 | // Created by Shaps Benkau on 23/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | private func radians(fromDegrees degrees: CGFloat) -> CGFloat { 11 | return degrees * CGFloat(Double.pi) / 180 12 | } 13 | 14 | internal struct CollapsibleSection { 15 | internal let group: PeekGroup 16 | internal let items: [CollapsibleItem] 17 | 18 | internal var isExpanded: Bool { 19 | get { return UserDefaults.standard.bool(forKey: group.title) } 20 | set { UserDefaults.standard.set(newValue, forKey: group.title) } 21 | } 22 | 23 | internal init(group: PeekGroup, items: [CollapsibleItem]) { 24 | self.group = group 25 | self.items = items 26 | } 27 | } 28 | 29 | internal struct CollapsibleItem { 30 | internal let title: String 31 | internal let attribute: Attribute 32 | } 33 | 34 | internal protocol CollapsibleSectionHeaderViewDelegate: class { 35 | func sectionHeader(_ view: CollapsibleSectionHeaderView, shouldToggleAt index: Int) 36 | } 37 | 38 | internal final class CollapsibleSectionHeaderView: UITableViewHeaderFooterView { 39 | 40 | internal weak var delegate: CollapsibleSectionHeaderViewDelegate? { 41 | didSet { 42 | imageView.isHidden = delegate == nil 43 | gesture.isEnabled = delegate != nil 44 | } 45 | } 46 | 47 | internal let label: UILabel 48 | private lazy var gesture: UITapGestureRecognizer = { 49 | UITapGestureRecognizer(target: self, action: #selector(handleTap(gesture:))) 50 | }() 51 | 52 | private let imageView: UIImageView 53 | 54 | override init(reuseIdentifier: String?) { 55 | label = UILabel(frame: .zero) 56 | imageView = UIImageView(image: nil) // collapsed/expanded indicator 57 | imageView.isHidden = true 58 | 59 | super.init(reuseIdentifier: reuseIdentifier) 60 | 61 | label.numberOfLines = 0 62 | 63 | contentView.addSubview(imageView, constraints: [ 64 | equal(\.centerYAnchor), 65 | equal(\.layoutMarginsGuide.trailingAnchor, \.trailingAnchor), 66 | ]) 67 | 68 | contentView.addSubview(label, constraints: [ 69 | equal(\.layoutMarginsGuide.leadingAnchor, \.leadingAnchor), 70 | equal(\.topAnchor, constant: -12), 71 | equal(\.bottomAnchor, constant: 12) 72 | ]) 73 | 74 | NSLayoutConstraint.activate([ 75 | label.trailingAnchor.constraint(equalTo: imageView.leadingAnchor, constant: 16) 76 | ]) 77 | 78 | gesture.isEnabled = false 79 | addGestureRecognizer(gesture) 80 | } 81 | 82 | @objc private func handleTap(gesture: UITapGestureRecognizer) { 83 | delegate?.sectionHeader(self, shouldToggleAt: tag) 84 | } 85 | 86 | func setExpanded(_ expanded: Bool, completion: (() -> Void)? = nil) { 87 | UIView.animate(withDuration: 0.25, animations: { 88 | let angle = radians(fromDegrees: -90) 89 | self.imageView.transform = expanded ? .identity : CGAffineTransform(rotationAngle: angle) 90 | }, completion: { _ in 91 | completion?() 92 | }) 93 | } 94 | 95 | func prepareHeader(for section: Int, theme: PeekTheme = .dark, delegate: CollapsibleSectionHeaderViewDelegate) { 96 | tag = section 97 | self.delegate = delegate 98 | 99 | let thickness: CGFloat = 1.5 100 | let size = CGSize(width: 13 + thickness, height: 8 + thickness) 101 | imageView.image = Images.disclosure(size: size, thickness: thickness, theme: theme) 102 | imageView.tintColor = theme.tintColor 103 | } 104 | 105 | internal required init?(coder aDecoder: NSCoder) { 106 | fatalError("init(coder:) has not been implemented") 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Inspectors/PeekInspectorCell.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Peek © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | /// Defines an inspector's cell used to represent a Peek property 26 | final class PeekInspectorCell: UITableViewCell { 27 | 28 | override var accessoryView: UIView? { 29 | didSet { setNeedsUpdateConstraints() } 30 | } 31 | 32 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 33 | super.init(style: .value1, reuseIdentifier: reuseIdentifier) 34 | 35 | indentationWidth = 17 36 | textLabel?.numberOfLines = 0 37 | detailTextLabel?.numberOfLines = 1 38 | 39 | clipsToBounds = true 40 | contentView.clipsToBounds = true 41 | 42 | let selectedView = UIView() 43 | selectedBackgroundView = selectedView 44 | } 45 | 46 | required init?(coder aDecoder: NSCoder) { 47 | fatalError("init(coder:) has not been implemented") 48 | } 49 | 50 | override func setHighlighted(_ highlighted: Bool, animated: Bool) { 51 | let color = accessoryView?.backgroundColor 52 | super.setHighlighted(highlighted, animated: animated) 53 | accessoryView?.backgroundColor = color 54 | } 55 | 56 | override func setSelected(_ selected: Bool, animated: Bool) { 57 | let color = accessoryView?.backgroundColor 58 | super.setSelected(selected, animated: animated) 59 | accessoryView?.backgroundColor = color 60 | } 61 | 62 | override func prepareForReuse() { 63 | super.prepareForReuse() 64 | 65 | textLabel?.text = nil 66 | detailTextLabel?.text = nil 67 | imageView?.image = nil 68 | accessoryView = nil 69 | accessoryType = .none 70 | editingAccessoryView = nil 71 | editingAccessoryType = .none 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Inspectors/PreviewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PreviewCell.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 24/02/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | internal final class PreviewCell: UITableViewCell { 11 | 12 | internal let previewImageView: UIImageView 13 | 14 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 15 | previewImageView = UIImageView() 16 | previewImageView.contentMode = .scaleAspectFit 17 | previewImageView.clipsToBounds = true 18 | 19 | previewImageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) 20 | previewImageView.setContentHuggingPriority(.defaultLow, for: .horizontal) 21 | previewImageView.setContentHuggingPriority(.required, for: .vertical) 22 | previewImageView.setContentCompressionResistancePriority(.required, for: .vertical) 23 | 24 | super.init(style: .value1, reuseIdentifier: reuseIdentifier) 25 | 26 | addSubview(previewImageView, constraints: [ 27 | equal(\.layoutMarginsGuide.leadingAnchor, \.leadingAnchor), 28 | equal(\.layoutMarginsGuide.trailingAnchor, \.trailingAnchor), 29 | equal(\.topAnchor, constant: -16), 30 | equal(\.bottomAnchor, constant: 16) 31 | ]) 32 | 33 | clipsToBounds = true 34 | contentView.clipsToBounds = true 35 | } 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | fatalError("init(coder:) has not been implemented") 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Overlays/Layout/PeekLayoutOverlayView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeekLayoutView.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 12/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | internal final class PeekLayoutOverlayView: PeekOverlayView { 11 | 12 | private lazy var layoutView: PeekLayoutView = { 13 | let view = PeekLayoutView(overlayView: self, borderColor: UIColor(white: 1, alpha: 0.5), borderWidth: 1, dashed: true, theme: theme) 14 | view.layer.zPosition = 0 15 | addSubview(view) 16 | return view 17 | }() 18 | 19 | override init(theme: PeekTheme = .dark) { 20 | super.init(theme: theme) 21 | // TODO: Needs to be true once its implemented 22 | allowsMultipleSelection = false 23 | } 24 | 25 | internal required init?(coder aDecoder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | override func refresh() { 30 | super.refresh() 31 | layoutView.setNeedsDisplay() 32 | } 33 | 34 | override func updateHighlights(animated: Bool) { 35 | super.updateHighlights(animated: animated) 36 | 37 | guard indexesForSelectedItems.count > 1, 38 | let first = indexesForSelectedItems.first, 39 | let second = indexesForSelectedItems.last else { 40 | return 41 | } 42 | 43 | let primary = viewModels[first] 44 | let secondary = viewModels[second] 45 | 46 | layoutView.frame = primary.frameInPeek(self).union(secondary.frameInPeek(self)) 47 | layoutView.primaryView = primary as? UIView 48 | layoutView.secondaryView = secondary as? UIView 49 | layoutView.primarySelectionView = primarySelectionView 50 | layoutView.secondarySelectionView = secondarySelectionView 51 | 52 | if animated { 53 | UIView.animate(withDuration: 0.2, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 1.1, options: .beginFromCurrentState, animations: { 54 | self.layoutView.refresh() 55 | }, completion: nil) 56 | } else { 57 | layoutView.refresh() 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Overlays/Layout/PeekMetricView.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | internal struct Metrics { 26 | 27 | var top: CGFloat 28 | var left: CGFloat 29 | var bottom: CGFloat 30 | var right: CGFloat 31 | 32 | init(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) { 33 | self.top = top 34 | self.left = left 35 | self.bottom = bottom 36 | self.right = right 37 | } 38 | 39 | } 40 | 41 | internal final class PeekMetricView: UIVisualEffectView { 42 | 43 | static let formatter: NumberFormatter = { 44 | let formatter = NumberFormatter() 45 | formatter.maximumFractionDigits = 1 46 | formatter.minimumFractionDigits = 0 47 | formatter.roundingIncrement = 0.5 48 | return formatter 49 | }() 50 | 51 | fileprivate let label: UILabel 52 | 53 | required init?(coder aDecoder: NSCoder) { 54 | fatalError() 55 | } 56 | 57 | init(theme: PeekTheme = .dark) { 58 | label = UILabel(frame: .zero) 59 | label.font = UIFont.systemFont(ofSize: 11, weight: .semibold) 60 | label.textColor = theme.primaryTextColor 61 | label.textAlignment = .center 62 | 63 | label.setContentCompressionResistancePriority(.required, for: .horizontal) 64 | label.setContentCompressionResistancePriority(.required, for: .vertical) 65 | 66 | label.setContentHuggingPriority(.required, for: .horizontal) 67 | label.setContentHuggingPriority(.required, for: .vertical) 68 | 69 | super.init(effect: UIBlurEffect(style: .extraLight)) 70 | 71 | layer.cornerRadius = 3 72 | layer.masksToBounds = true 73 | layer.zPosition = 100 74 | 75 | contentView.addSubview(label, constraints: [ 76 | equal(\.leadingAnchor, constant: -2), equal(\.trailingAnchor, constant: 2), 77 | equal(\.topAnchor, constant: -1), equal(\.bottomAnchor, constant: 1) 78 | ]) 79 | } 80 | 81 | internal func apply(value: CGFloat) { 82 | label.text = PeekMetricView.formatter.string(from: NSNumber(value: Float(value))) 83 | label.sizeToFit() 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Overlays/PeekButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeekButton.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 07/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | internal final class PeekButton: UIControl { 11 | 12 | private var feedbackGenerator: Any? 13 | 14 | @available(iOS 10.0, *) 15 | private func haptic() -> UIImpactFeedbackGenerator? { 16 | return feedbackGenerator as? UIImpactFeedbackGenerator 17 | } 18 | 19 | private lazy var tapGesture: UITapGestureRecognizer = { 20 | return UITapGestureRecognizer(target: self, action: #selector(handleTap(gesture:))) 21 | }() 22 | 23 | private lazy var vibrancyView: UIVisualEffectView = { 24 | return UIVisualEffectView(effect: UIBlurEffect(style: .dark)) 25 | }() 26 | 27 | private lazy var visualEffectView: UIVisualEffectView = { 28 | let blur = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) 29 | blur.contentView.addSubview(vibrancyView, constraints: [ 30 | equal(\.leadingAnchor), equal(\.trailingAnchor), 31 | equal(\.topAnchor), equal(\.bottomAnchor) 32 | ]) 33 | return blur 34 | }() 35 | 36 | private lazy var imageView: UIImageView = { 37 | return UIImageView(image: Images.attributes) 38 | }() 39 | 40 | private let theme: PeekTheme 41 | 42 | internal init(theme: PeekTheme) { 43 | self.theme = theme 44 | super.init(frame: .zero) 45 | 46 | if #available(iOS 11.0, *) { 47 | accessibilityIgnoresInvertColors = true 48 | } 49 | 50 | vibrancyView.contentView.addSubview(imageView, constraints: [ 51 | equal(\.centerXAnchor), equal(\.centerYAnchor) 52 | ]) 53 | 54 | addSubview(visualEffectView, constraints: [ 55 | equal(\.leadingAnchor), equal(\.trailingAnchor), 56 | equal(\.topAnchor), equal(\.bottomAnchor) 57 | ]) 58 | 59 | addGestureRecognizer(tapGesture) 60 | backgroundColor = .clear 61 | } 62 | 63 | required init?(coder aDecoder: NSCoder) { 64 | fatalError("init(coder:) has not been implemented") 65 | } 66 | 67 | @objc private func handleTap(gesture: UITapGestureRecognizer) { 68 | sendActions(for: .touchUpInside) 69 | } 70 | 71 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 72 | super.touchesBegan(touches, with: event) 73 | setTransform(CGAffineTransform(scaleX: 1.5, y: 1.5)) 74 | 75 | if #available(iOS 10.0, *) { 76 | feedbackGenerator = UIImpactFeedbackGenerator(style: .light) 77 | haptic()?.impactOccurred() 78 | } 79 | } 80 | 81 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 82 | super.touchesEnded(touches, with: event) 83 | setTransform(.identity) 84 | feedbackGenerator = nil 85 | } 86 | 87 | override func touchesCancelled(_ touches: Set, with event: UIEvent?) { 88 | super.touchesCancelled(touches, with: event) 89 | setTransform(.identity) 90 | feedbackGenerator = nil 91 | } 92 | 93 | private func setTransform(_ transform: CGAffineTransform) { 94 | let damping: CGFloat = transform == .identity ? 1 : 0.45 95 | UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: damping, initialSpringVelocity: 1, options: [.beginFromCurrentState, .allowUserInteraction], animations: { 96 | self.transform = transform 97 | }, completion: nil) 98 | } 99 | 100 | override var intrinsicContentSize: CGSize { 101 | return CGSize(width: 60, height: 60) 102 | } 103 | 104 | override func layoutSubviews() { 105 | super.layoutSubviews() 106 | 107 | let size = bounds.height / 2 108 | 109 | visualEffectView.layer.cornerRadius = size 110 | visualEffectView.layer.masksToBounds = true 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Overlays/PeekSelectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeekSelectionView.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 12/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | internal class PeekSelectionView: UIView { 11 | 12 | private let borderWidth: CGFloat 13 | private let borderColor: UIColor 14 | private let dashed: Bool 15 | 16 | internal init(borderColor: UIColor?, borderWidth: CGFloat, dashed: Bool = false) { 17 | self.borderColor = borderColor ?? .white 18 | self.borderWidth = borderWidth 19 | self.dashed = dashed 20 | 21 | super.init(frame: .zero) 22 | 23 | backgroundColor = .clear 24 | layer.zPosition = 20 25 | 26 | guard !dashed else { return } 27 | 28 | layer.borderWidth = borderWidth 29 | layer.borderColor = self.borderColor.cgColor 30 | layer.cornerRadius = borderWidth * 2 31 | } 32 | 33 | internal required init?(coder aDecoder: NSCoder) { 34 | fatalError("init(coder:) has not been implemented") 35 | } 36 | 37 | override func draw(_ rect: CGRect) { 38 | super.draw(rect) 39 | guard dashed else { return } 40 | 41 | let inset = borderWidth / 2 42 | let path = UIBezierPath(roundedRect: rect.insetBy(dx: inset, dy: inset), cornerRadius: borderWidth * 2) 43 | 44 | if dashed { 45 | path.setLineDash([4, 4], count: 2, phase: 0) 46 | } 47 | 48 | borderColor.setStroke() 49 | path.stroke() 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Overlays/Text/LayoutManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LayoutManager.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 13/03/2018. 6 | // 7 | 8 | import Foundation 9 | import CoreText 10 | 11 | final class LayoutManager: NSLayoutManager { 12 | 13 | override func drawBackground(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) { 14 | super.drawBackground(forGlyphRange: glyphsToShow, at:origin) 15 | 16 | guard let text = textStorage?.string else { return } 17 | 18 | enumerateLineFragments(forGlyphRange: glyphsToShow) 19 | { (rect: CGRect, usedRect: CGRect, textContainer: NSTextContainer, glyphRange: NSRange, stop: UnsafeMutablePointer) -> Void in 20 | 21 | let characterRange = self.characterRange(forGlyphRange: glyphRange, actualGlyphRange: nil) 22 | 23 | // Draw invisible tab space characters 24 | 25 | let line = (text as NSString).substring(with: characterRange) 26 | 27 | do { 28 | let expr = try NSRegularExpression(pattern: "\t", options: []) 29 | 30 | expr.enumerateMatches(in: line, options: .reportProgress, range: characterRange) 31 | { result, flags, stop in 32 | if let result = result { 33 | 34 | let range = NSMakeRange(result.range.location + characterRange.location, result.range.length) 35 | let characterRect = self.boundingRect(forGlyphRange: range, in: textContainer) 36 | 37 | let symbol = "\u{21E5}" 38 | let attrs = [NSAttributedString.Key.foregroundColor : UIColor.red ] 39 | let height = (symbol as NSString).size(withAttributes: attrs).height 40 | let rect = characterRect.offsetBy(dx: 1, dy: height * 0.5) 41 | symbol.draw(in: rect, withAttributes: attrs) 42 | } 43 | 44 | } 45 | 46 | } catch let error as NSError { 47 | print(error.localizedDescription) 48 | } 49 | 50 | } 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Overlays/Text/PeekTextOverlayView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeekTextOverlay.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 13/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | internal final class PeekTextOverlayView: PeekOverlayView { 11 | 12 | private var textView: UITextView? 13 | 14 | internal init(textView: UITextView) { 15 | self.textView = textView 16 | super.init() 17 | } 18 | 19 | internal required init?(coder aDecoder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/Reporting/ReportActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReportActivity.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 06/03/2018. 6 | // 7 | 8 | import UIKit 9 | import MobileCoreServices 10 | 11 | internal final class ReportActivity: NSObject, UIActivityItemSource { 12 | 13 | private let report: Report 14 | 15 | internal init(report: Report) { 16 | self.report = report 17 | } 18 | 19 | func activityViewController(_ activityViewController: UIActivityViewController, thumbnailImageForActivityType activityType: UIActivity.ActivityType?, suggestedSize size: CGSize) -> UIImage? { 20 | return report.snapshot?.resized(to: size) 21 | } 22 | 23 | func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { 24 | return "Peek Report" 25 | } 26 | 27 | func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { 28 | guard let type = activityType else { return report.plainText } 29 | 30 | switch type { 31 | case .mail: 32 | return report.html 33 | default: 34 | return report.plainText 35 | } 36 | } 37 | 38 | func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String { 39 | let formatter = DateFormatter() 40 | formatter.dateStyle = .short 41 | return "Peek Report: \(formatter.string(from: Date()))" 42 | } 43 | 44 | func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String { 45 | guard let type = activityType else { return kUTTypePlainText as String } 46 | 47 | switch type { 48 | case .mail: 49 | return kUTTypeHTML as String 50 | default: 51 | return kUTTypePlainText as String 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Pod/Classes/Controllers & Views/UIViewController+Modal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Modal.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 21/02/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIViewController { 11 | 12 | func presentModal(_ viewControllerToPresent: UIViewController, from sourceView: UIView?, animated: Bool, completion: (() -> Void)?) { 13 | let controller = PeekPresentationController(presentedViewController: viewControllerToPresent, presenting: presentingViewController) 14 | 15 | withExtendedLifetime(controller) { _ in 16 | viewControllerToPresent.transitioningDelegate = controller 17 | self.present(viewControllerToPresent, animated: animated, completion: completion) 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Pod/Classes/GraphicsRenderer/Platforms.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 03/10/2016 Snippex Ltd 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #if os(OSX) 24 | import AppKit 25 | internal typealias Image = NSImage 26 | internal typealias Screen = NSScreen 27 | 28 | extension NSScreen { 29 | internal static var main: NSScreen { 30 | return self.main 31 | } 32 | 33 | internal var scale: CGFloat { 34 | return backingScaleFactor 35 | } 36 | } 37 | 38 | extension NSImage { 39 | internal func pngRepresentation() -> Data? { 40 | return NSBitmapImageRep(data: tiffRepresentation!)?.representation(using: .png, properties: [:]) 41 | } 42 | 43 | internal func jpgRepresentation(quality: CGFloat) -> Data? { 44 | return NSBitmapImageRep(data: tiffRepresentation!)?.representation(using: .jpeg, properties: [.compressionFactor: quality]) 45 | } 46 | } 47 | #else 48 | import UIKit 49 | internal typealias Image = UIImage 50 | internal typealias Screen = UIScreen 51 | 52 | extension UIImage { 53 | internal func pngRepresentation() -> Data? { 54 | return pngData() 55 | } 56 | 57 | internal func jpgRepresentation(quality: CGFloat) -> Data? { 58 | return jpegData(compressionQuality: quality) 59 | } 60 | } 61 | #endif 62 | 63 | extension CGContext { 64 | 65 | internal static var current: CGContext? { 66 | #if os(OSX) 67 | return NSGraphicsContext.current?.cgContext 68 | #else 69 | return UIGraphicsGetCurrentContext() 70 | #endif 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Pod/Classes/GraphicsRenderer/Renderer.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 03/10/2016 Snippex Ltd 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import Foundation 24 | 25 | /** 26 | Represents a Renderer error 27 | 28 | - missingContext: The context could not be found or created 29 | */ 30 | internal enum RendererError: Error { 31 | case missingContext 32 | case invalidURL 33 | } 34 | 35 | /** 36 | * Defines a renderer format 37 | */ 38 | internal protocol RendererFormat: class { 39 | 40 | /** 41 | Returns a default instance, configured for the current device 42 | 43 | - returns: A new renderer format 44 | */ 45 | static func `default`() -> Self 46 | 47 | /// Returns the drawable bounds 48 | var bounds: CGRect { get } 49 | 50 | } 51 | 52 | 53 | /** 54 | * Represents a drawable -- used to add drawing support to CGContext, RendererContext and UIGraphicsImageRendererContext 55 | */ 56 | internal protocol RendererDrawable { 57 | 58 | var cgContext: CGContext { get } 59 | func fill(_ rect: CGRect) 60 | func fill(_ rect: CGRect, blendMode: CGBlendMode) 61 | func stroke(_ rect: CGRect) 62 | func stroke(_ rect: CGRect, blendMode: CGBlendMode) 63 | func clip(to rect: CGRect) 64 | } 65 | 66 | 67 | /** 68 | * Represents a renderer context, which provides additional drawing methods as well as access to the underlying CGContext 69 | */ 70 | internal protocol RendererContext: class, RendererDrawable { 71 | associatedtype Format: RendererFormat 72 | var format: Format { get } 73 | } 74 | 75 | extension CGContext: RendererDrawable { 76 | internal var cgContext: CGContext { 77 | return self 78 | } 79 | } 80 | 81 | #if os(iOS) || os(tvOS) 82 | @available(iOS 10.0, *) 83 | extension UIGraphicsImageRendererContext: RendererDrawable { } 84 | #endif 85 | 86 | 87 | /** 88 | * Represents a renderer 89 | */ 90 | internal protocol Renderer: class { 91 | 92 | /// The associated context type this renderer will use 93 | associatedtype Context: RendererContext 94 | 95 | /// Returns the format associated with this renderer 96 | var format: Context.Format { get } 97 | 98 | /// Returns true if this renderer may be used to generate CGImageRefs 99 | var allowsImageOutput: Bool { get } 100 | 101 | init(bounds: CGRect) 102 | } 103 | 104 | extension Renderer { 105 | 106 | /// Default implementation returns false 107 | internal var allowsImageOutput: Bool { 108 | return false 109 | } 110 | 111 | /** 112 | Default implementation returns false 113 | */ 114 | internal static func context(with format: RendererFormat) -> CGContext? { 115 | return nil 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /Pod/Classes/GraphicsRenderer/RendererDrawable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 03/10/2016 Snippex Ltd 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import Foundation 24 | 25 | extension RendererDrawable { 26 | 27 | /// Fills the specified rect 28 | /// 29 | /// - Parameter rect: The rect to fill 30 | internal func fill(_ rect: CGRect) { 31 | fill(rect, blendMode: .normal) 32 | } 33 | 34 | /// Fills the specified rect with the given blend mode 35 | /// 36 | /// - Parameters: 37 | /// - rect: The rect to fill 38 | /// - blendMode: The blend mode to apply to this fill 39 | internal func fill(_ rect: CGRect, blendMode: CGBlendMode) { 40 | cgContext.saveGState() 41 | cgContext.setBlendMode(blendMode) 42 | cgContext.fill(rect) 43 | cgContext.restoreGState() 44 | } 45 | 46 | /// Strokes the specified rect 47 | /// 48 | /// - Parameter rect: The rect to stroke 49 | internal func stroke(_ rect: CGRect) { 50 | stroke(rect, blendMode: .normal) 51 | } 52 | 53 | /// Strokes the specified rect with the given blend mode 54 | /// 55 | /// - Parameters: 56 | /// - rect: The rect to stroke 57 | /// - blendMode: The blend more to apply to this stroke 58 | internal func stroke(_ rect: CGRect, blendMode: CGBlendMode) { 59 | cgContext.saveGState() 60 | cgContext.setBlendMode(blendMode) 61 | cgContext.stroke(rect.insetBy(dx: 0.5, dy: 0.5)) 62 | cgContext.restoreGState() 63 | } 64 | 65 | /// Clips the context to the specified rect 66 | /// 67 | /// - Parameter rect: The rect to clip to 68 | internal func clip(to rect: CGRect) { 69 | cgContext.saveGState() 70 | cgContext.clip(to: rect) 71 | cgContext.restoreGState() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/Bool+Toggle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bool+Toggle.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 04/05/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bool { 11 | 12 | /// Equivalent to `someBool = !someBool` 13 | /// 14 | /// Useful when operating on long chains: 15 | /// 16 | /// myVar.prop1.prop2.enabled.toggle() 17 | mutating func toggle() { 18 | self = !self 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/CGSize+Resize.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGRect+Resize.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 20/03/2018. 6 | // 7 | 8 | import CoreGraphics 9 | 10 | internal enum ScaleMode: Int { 11 | 12 | case scaleToFill 13 | case scaleAspectFit 14 | case scaleAspectFill 15 | case center 16 | 17 | } 18 | 19 | extension CGSize { 20 | 21 | /** 22 | Returns a new size, scaled to the specified size using the given scale mode 23 | 24 | - parameter size: The size to scale to 25 | - parameter mode: The scale mode 26 | 27 | - returns: The resulting size 28 | */ 29 | internal func scaledTo(size: CGSize, scaleMode mode: ScaleMode) -> CGSize { 30 | var w: CGFloat, h: CGFloat 31 | 32 | switch mode { 33 | case .scaleToFill: 34 | w = size.width 35 | h = size.height 36 | case .scaleAspectFit: 37 | let mW = size.width / self.width 38 | let mH = size.height / self.height 39 | 40 | if mH < mW { 41 | w = mH * self.width 42 | h = size.height 43 | } else { 44 | h = mW * self.height 45 | w = size.width 46 | } 47 | case .scaleAspectFill: 48 | let mW = size.width / self.width 49 | let mH = size.height / self.height 50 | 51 | if mH > mW { 52 | w = mH * self.width 53 | h = size.height 54 | } else { 55 | h = mW * self.height 56 | w = size.width 57 | } 58 | case .center: 59 | w = self.height 60 | h = self.width 61 | } 62 | 63 | return CGSize(width: w, height: h) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/Collection+NilOrEmpty.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Collection+NilOrEmpty.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 04/05/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | internal extension Optional where Wrapped: Collection { 11 | 12 | /// Equivalent to `if let value = value, !value.isEmpty` 13 | /// 14 | /// Useful when you need to know whether or not an underlying value exists at all. 15 | /// 16 | /// string.isNilOrEmpty 17 | /// 18 | /// Returns true if `string` is nil or empty. False otherwise 19 | var isNilOrEmpty: Bool { 20 | switch self { 21 | case .some(let wrapped): return wrapped.isEmpty 22 | case .none: return true 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/Color+Extensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIColor { 26 | 27 | internal var hslComponents:(hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat) { 28 | var h: CGFloat = 0, s: CGFloat = 0, l: CGFloat = 0, a: CGFloat = 0 29 | getHue(&h, saturation: &s, brightness: &l, alpha: &a) 30 | return (h, s, l, a) 31 | } 32 | 33 | internal var rgbComponents:(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { 34 | var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0 35 | getRed(&r, green: &g, blue: &b, alpha: &a) 36 | return (r, g, b, a) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/Enum+Cases.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Enum+Cases.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 26/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | extension RawRepresentable where RawValue == Int, Self: Hashable { 11 | 12 | private static func cases() -> AnySequence { 13 | return AnySequence { () -> AnyIterator in 14 | var raw = 0 15 | return AnyIterator { 16 | let current: Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: self, capacity: 1) { $0.pointee } } 17 | guard current.hashValue == raw else { 18 | return nil 19 | } 20 | raw += 1 21 | return current 22 | } 23 | } 24 | } 25 | 26 | internal static var all: [Self] { 27 | return Array(self.cases()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/NSNumber+Extensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import Foundation 24 | 25 | extension NSNumber { 26 | 27 | func isBool() -> Bool { 28 | let boolID = CFBooleanGetTypeID() 29 | let numID = CFGetTypeID(self) 30 | return numID == boolID 31 | } 32 | 33 | func isFloat() -> Bool { 34 | switch CFNumberGetType(self) { 35 | case .cgFloatType, .doubleType, .float32Type, .float64Type, .floatType: 36 | return true 37 | default: 38 | return false 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/NSObject+Extensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension NSObjectProtocol { 26 | 27 | static func ObjClassName() -> String { 28 | return NSStringFromClass(self).components(separatedBy: ".").last! 29 | } 30 | 31 | func ObjClassName() -> String { 32 | return Self.ObjClassName() 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/PeekTapGestureRecognizer.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | import UIKit.UIGestureRecognizerSubclass 25 | 26 | final class PeekTapGestureRecognizer: UITapGestureRecognizer { 27 | 28 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 29 | super.touchesBegan(touches, with: event!) 30 | 31 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) { 32 | if self.state != .recognized { 33 | self.state = .failed 34 | } 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/String+Extensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import Foundation 24 | 25 | extension String { 26 | 27 | // Converts a camel case string to a capitalized one -- e.g. 'firstName' -> 'First Name' 28 | static func capitalized(_ camelCase: String) -> String { 29 | let chars = CharacterSet.uppercaseLetters 30 | var string = camelCase.components(separatedBy: ".").last ?? camelCase 31 | let peekPrefix = "peek_" 32 | let supportPrefix = "supports" 33 | 34 | if string.contains(peekPrefix) { 35 | string = String(string.dropFirst(peekPrefix.count)) 36 | } 37 | 38 | if string.contains(supportPrefix) { 39 | string = String(string.dropFirst(supportPrefix.count)) 40 | } 41 | 42 | while let range = string.rangeOfCharacter(from: chars) { 43 | let char = string[range] 44 | string.replaceSubrange(range, with: " " + char.lowercased()) 45 | } 46 | 47 | return string.capitalized.trimmingCharacters(in: CharacterSet.whitespaces) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/UIDevice+Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIDevice+Extensions.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 26/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | extension UIDevice { 11 | 12 | internal var isSimulator: Bool { 13 | var isSimulator = false 14 | #if (arch(i386) || arch(x86_64)) 15 | isSimulator = true 16 | #endif 17 | return isSimulator 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/UIImage+Resize.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Resize.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 20/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIImage { 11 | 12 | public func resized(to preferredSize: CGSize) -> UIImage { 13 | let newSize = size.scaledTo(size: preferredSize, scaleMode: .scaleAspectFit) 14 | UIGraphicsBeginImageContextWithOptions(newSize, true, UIScreen.main.scale) 15 | let rect = CGRect(origin: .zero, size: newSize) 16 | draw(in: rect) 17 | let image = UIGraphicsGetImageFromCurrentImageContext() 18 | UIGraphicsEndImageContext() 19 | return image ?? self 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/UIView+Extensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIView { 26 | 27 | /** 28 | Returns the UIViewController that owns this view 29 | 30 | - returns: The owning view controller 31 | */ 32 | @objc internal func owningViewController() -> UIViewController? { 33 | var responder: UIResponder? = self 34 | 35 | while !(responder is UIViewController) && superview != nil { 36 | if let next = responder?.next { 37 | responder = next 38 | } 39 | } 40 | 41 | return responder as? UIViewController 42 | } 43 | 44 | /** 45 | Returns the CGRect representing this view, within the coordinates of Peek's overlay view 46 | 47 | - parameter view: The view to translate 48 | 49 | - returns: A CGRect in the coordinate space of Peek's overlay view 50 | */ 51 | func frameInPeek(_ view: UIView) -> CGRect { 52 | return convert(bounds, to: view) 53 | } 54 | 55 | /** 56 | Returns the CGRect representing this view, excluding the current CGAffineTransform being applied to it 57 | 58 | - parameter view: The view to translate 59 | 60 | - returns: A CGRect in the coordinator space of Peek's overlay view 61 | */ 62 | func frameInPeekWithoutTransform(_ view: UIView) -> CGRect { 63 | let center = self.center 64 | let size = self.bounds.size 65 | let rect = CGRect(x: center.x - size.width / 2, y: center.y - size.height / 2, width: size.width, height: size.height) 66 | 67 | if let superview = self.superview { 68 | return superview.convert(rect, to: view) 69 | } 70 | 71 | return CGRect.zero 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/UIViewController+Extensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIViewController { 26 | 27 | /** 28 | Returns the top view controller currently on screen in the application 29 | 30 | - returns: The top view controller 31 | */ 32 | func topViewController() -> UIViewController { 33 | 34 | if let controller = self as? UINavigationController { 35 | return controller.topViewController?.topViewController() ?? controller 36 | } 37 | 38 | if let controller = self as? UITabBarController { 39 | return controller.selectedViewController?.topViewController() ?? controller 40 | } 41 | 42 | if let controller = presentedViewController { 43 | return controller.topViewController() 44 | } 45 | 46 | return self 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Pod/Classes/Helpers/UIWindow+Extensions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | @objc extension UIWindow { 26 | 27 | /// Returns the Peek instance associated with this window 28 | @objc public var peek: Peek { 29 | return objc_getAssociatedObject(self, &PeekAssociationKey.Peek) as? Peek ?? { 30 | let associatedProperty = Peek(window: self) 31 | objc_setAssociatedObject(self, &PeekAssociationKey.Peek, associatedProperty, .OBJC_ASSOCIATION_RETAIN) 32 | return associatedProperty 33 | }() 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Pod/Classes/Model/ContextDataSource.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import Foundation 24 | 25 | /// Provides a data-source for representing properties in a Context -- used by InspectorViewController 26 | internal final class ContextDataSource { 27 | 28 | internal private(set) var sections: [CollapsibleSection] 29 | 30 | private init(sections: [CollapsibleSection]) { 31 | self.sections = sections 32 | } 33 | 34 | internal init(coordinator: PeekCoordinator) { 35 | sections = Group.all.compactMap { group in 36 | guard let peekGroup = coordinator.groupsMapping[group] else { return nil } 37 | 38 | let items = peekGroup.attributes 39 | .map { CollapsibleItem(title: $0.title, attribute: $0) } 40 | 41 | return CollapsibleSection(group: peekGroup, items: items) 42 | } 43 | } 44 | 45 | internal func filtered(by searchTerm: String?) -> ContextDataSource { 46 | guard let searchText = searchTerm?.trimmingCharacters(in: .whitespaces), 47 | !searchText.isEmpty else { 48 | return self 49 | } 50 | 51 | let sections: [CollapsibleSection] = self.sections.compactMap { 52 | let items = $0.items.filter { 53 | $0.title.localizedCaseInsensitiveContains(searchText) 54 | } 55 | 56 | guard !items.isEmpty else { return nil } 57 | return CollapsibleSection(group: $0.group, items: items) 58 | } 59 | 60 | return ContextDataSource(sections: sections) 61 | } 62 | 63 | internal func attribute(at indexPath: IndexPath) -> Attribute { 64 | return sections[indexPath.section].items[indexPath.item].attribute 65 | } 66 | 67 | internal func setExpanded(_ expanded: Bool, for section: Int) { 68 | sections[section].isExpanded = expanded 69 | } 70 | 71 | internal func toggleVisibility(forSection section: Int) { 72 | sections[section].isExpanded.toggle() 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Pod/Classes/Model/UIView+Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Ignore.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 26/03/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | 12 | /** 13 | Determines if Peek should ignore this view when parsing it into a model 14 | 15 | - parameter peek: The Peek instance 16 | 17 | - returns: Returns true if Peek should ignore this view, false otherwise 18 | */ 19 | internal override func isVisibleInOverlay(options: PeekOptions) -> Bool { 20 | let isContainer = isMember(of: UIView.self) && subviews.count > 0 21 | if isContainer && options.ignoresContainerViews { return false } 22 | 23 | let isInvisible = isHidden || alpha == 0 || frame.equalTo(CGRect.zero) 24 | if isInvisible { return false } 25 | 26 | let isTableViewOrCell = isMember(of: UITableViewCell.self) || isMember(of: UITableView.self) 27 | if isTableViewOrCell { return false } 28 | 29 | let isCollectionView = isMember(of: UICollectionView.self) 30 | if isCollectionView { return false } 31 | 32 | let isFullScreen = frame.equalTo(window?.bounds ?? UIScreen.main.bounds) 33 | if isFullScreen { return false } 34 | 35 | if String(describing: classForCoder).hasPrefix("_UIModern") { return true } 36 | 37 | let blacklist = [ "UIPickerTableView", "UIPickerColumnView", "UITableViewCellContentView" ] 38 | let className = String(describing: classForCoder) 39 | 40 | if className.hasPrefix("_") || blacklist.contains(className) { 41 | return false 42 | } 43 | 44 | let invalidContainerClassNames = [ "UINavigationButton" ] 45 | var superview = self.superview 46 | 47 | while superview != nil { 48 | if superview?.isComponent == true { 49 | return false 50 | } 51 | 52 | // also need to check private internal classes 53 | for className in invalidContainerClassNames { 54 | if let klass = NSClassFromString(className) { 55 | if superview?.isMember(of: klass) ?? false { 56 | return false 57 | } 58 | } 59 | } 60 | 61 | superview = superview?.superview 62 | } 63 | 64 | return true 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /Pod/Classes/PeekOptions.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import Foundation 24 | 25 | /** 26 | Available activation modes 27 | 28 | - Auto: Peek will use a shake gesture when running in the Simulator, and the volume controls on a device 29 | - Shake: Peek will use a shake gesture on both the Simulator and a device 30 | */ 31 | @objc public enum PeekActivationMode: Int { 32 | /// Peek will use a shake gesture when running in the Simulator, and the volume controls on a device 33 | case auto = 0 34 | /// Peek will use a shake gesture on both the Simulator and a device 35 | case shake 36 | } 37 | 38 | @objc public enum PeekTheme: Int { 39 | case dark = 0 40 | case black 41 | case light 42 | } 43 | 44 | /// Defines various options to use when enabling Peek 45 | public final class PeekOptions: NSObject { 46 | 47 | /// Defines how peek looks (.black mode is optimised for OLED displays). Defaults to .dark 48 | @objc public var theme: PeekTheme = .dark 49 | 50 | /// Defines how Peek is activated/de-activated. Defaults to auto 51 | @objc public var activationMode: PeekActivationMode = .auto 52 | 53 | /// When this is true, views that are not subclassed but contain subviews, will be ignored. Defaults to false 54 | @objc public var ignoresContainerViews = false 55 | 56 | /// You can provide meta data that will be attached to every report. This is useful for passing additional info about the app, e.g. Environment, etc... 57 | @objc public var metadata: [String: String] = [:] 58 | 59 | // MARK: Obsoletions 60 | 61 | @available(*, deprecated, renamed: "ignoresContainerViews") 62 | @objc public var shouldIgnoreContainers: Bool { 63 | get { return ignoresContainerViews } 64 | set { ignoresContainerViews = newValue } 65 | } 66 | 67 | @available(*, deprecated) 68 | @objc public var includeScreenshot = true 69 | @available(*, deprecated, message: "Defaults to UIScreen.main.scale") 70 | @objc public var screenshotScale = UIScreen.main.scale 71 | @available(*, deprecated, message: "Peek now uses the built in UIActivityViewController") 72 | @objc public var slackUserName = "Peek" 73 | @available(*, deprecated, message: "Peek now uses the built in UIActivityViewController") 74 | @objc public var slackRecipient: String? 75 | @available(*, deprecated, message: "Peek now uses the built in UIActivityViewController") 76 | @objc public var slackWebHookURL: URL? 77 | @available(*, deprecated, message: "Peek now uses the built in UIActivityViewController") 78 | @objc public var emailRecipients: [String]? 79 | @available(*, deprecated, message: "Peek now uses the built in UIActivityViewController") 80 | @objc public var emailSubject: String? 81 | @available(*, deprecated, message: "Peek now uses the built in UIActivityViewController") 82 | @objc public var slackImageUploader: ((URLSession, UIImage) -> URL?)? 83 | @available(*, deprecated, renamed: "metadata") 84 | @objc public var reportMetaData: [String: String]? 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Pod/Classes/PeekTheme+Colors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PeekTheme+Colors.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 26/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | extension PeekTheme { 11 | 12 | internal var overlayBackgroundColor: UIColor? { 13 | return .black 14 | } 15 | 16 | internal var overlayTintColor: UIColor? { 17 | return Color(literalRed: 135, green: 252, blue: 112).system 18 | } 19 | 20 | internal var backgroundColor: UIColor? { 21 | switch self { 22 | case .dark: return Color(hex: "1c1c1c")!.system 23 | case .black: return .black 24 | case .light: return .white 25 | } 26 | } 27 | 28 | internal var selectedBackgroundColor: UIColor? { 29 | switch self { 30 | case .dark: return UIColor(white: 1, alpha: 0.1) 31 | case .black: return UIColor(white: 1, alpha: 0.1) 32 | case .light: return UIColor(white: 0, alpha: 0.1) 33 | } 34 | } 35 | 36 | internal var separatorColor: UIColor? { 37 | switch self { 38 | case .dark: return UIColor(white: 1, alpha: 0.1) 39 | case .black: return UIColor(white: 1, alpha: 0.1) 40 | case .light: return UIColor(white: 0, alpha: 0.1) 41 | } 42 | } 43 | 44 | internal func titleTextColor(isEditing: Bool) -> UIColor? { 45 | switch self { 46 | case .dark: return .white 47 | case .black: return .white 48 | case .light: return isEditing ? .white : .black 49 | } 50 | } 51 | 52 | internal var primaryTextColor: UIColor? { 53 | switch self { 54 | case .dark: return .white 55 | case .black: return .white 56 | case .light: return .black 57 | } 58 | } 59 | 60 | internal var secondaryTextColor: UIColor? { 61 | switch self { 62 | case .dark: return UIColor(white: 1, alpha: 0.6) 63 | case .black: return UIColor(white: 1, alpha: 0.6) 64 | case .light: return Color(hex: "1c1c1c")!.system 65 | } 66 | } 67 | 68 | internal var tintColor: UIColor? { 69 | switch self { 70 | case .dark: return Color(literalRed: 135, green: 252, blue: 112).system 71 | case .black: return Color(literalRed: 135, green: 252, blue: 112).system 72 | case .light: return Color(hex: "4CD863")!.system 73 | } 74 | } 75 | 76 | internal var editingColor: UIColor? { 77 | switch self { 78 | case .dark: return Color(hex: "4CD863")!.system 79 | case .black: return Color(hex: "4CD863")!.system 80 | case .light: return Color(hex: "4CD863")!.system 81 | } 82 | } 83 | 84 | internal var editingCounterColor: UIColor? { 85 | return Color(hex: "3EB454")!.system 86 | } 87 | 88 | internal var disclosureColor: UIColor? { 89 | switch self { 90 | case .dark: return UIColor(white: 1, alpha: 0.6) 91 | case .black: return UIColor(white: 1, alpha: 0.6) 92 | case .light: return UIColor(red: 200/255, green: 200/255, blue: 200/255, alpha: 1) 93 | } 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/Bundle+Peekable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+Peekable.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 26/02/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Bundle { 11 | 12 | @objc var backgroundModes: [String]? { 13 | return infoDictionary?["UIBackgroundModes"] as? [String] 14 | } 15 | 16 | @objc var capabilities: [String]? { 17 | return infoDictionary?["UIRequiredDeviceCapabilities"] as? [String] 18 | } 19 | 20 | @objc var supportedDevices: String { 21 | let families = infoDictionary?["UIDeviceFamily"] as? [Int] 22 | let iphone = families?.contains(1) ?? false 23 | let ipad = families?.contains(2) ?? false 24 | 25 | if iphone && ipad { 26 | return "Universal" 27 | } else if iphone { 28 | return "iPhone" 29 | } else { 30 | return "iPad" 31 | } 32 | } 33 | 34 | @objc var supportsHealthKit: Bool { 35 | return capabilities?.contains("healthkit") ?? false 36 | } 37 | 38 | @objc var supportsGameKit: Bool { 39 | return capabilities?.contains("gamekit") ?? false 40 | } 41 | 42 | @objc var supportsBackgroundAudio: Bool { 43 | return backgroundModes?.contains("audio") ?? false 44 | } 45 | 46 | @objc var supportsExternalAccessory: Bool { 47 | return backgroundModes?.contains("external-accessory") ?? false 48 | } 49 | 50 | @objc var supportsBackgroundFetch: Bool { 51 | return backgroundModes?.contains("fetch") ?? false 52 | } 53 | 54 | @objc var supportsLocation: Bool { 55 | return backgroundModes?.contains("location") ?? false 56 | } 57 | 58 | @objc var supportsNewsstand: Bool { 59 | return backgroundModes?.contains("newsstand-content") ?? false 60 | } 61 | 62 | @objc var supportsPushNotifications: Bool { 63 | return backgroundModes?.contains("remote-notification") ?? false 64 | } 65 | 66 | @objc var supportsVoip: Bool { 67 | return backgroundModes?.contains("voip") ?? false 68 | } 69 | 70 | @objc var supportsBluetooth: Bool { 71 | return backgroundModes?.contains("bluetooth-central") ?? false 72 | } 73 | 74 | @objc var sharesDataViaBluetooth: Bool { 75 | return backgroundModes?.contains("bluetooth-peripheral") ?? false 76 | } 77 | 78 | @objc var appName: String { 79 | return infoDictionary?["CFBundleName"] as? String ?? "Unknown" 80 | } 81 | 82 | @objc var appIcon: UIImage? { 83 | guard let iconsDictionary = Bundle.main.infoDictionary?["CFBundleIcons"] as? NSDictionary, 84 | let primaryIconsDictionary = iconsDictionary["CFBundlePrimaryIcon"] as? NSDictionary, 85 | let iconFiles = primaryIconsDictionary["CFBundleIconFiles"] as? NSArray, 86 | // First will be smallest for the device class, last will be the largest for device class 87 | let lastIcon = iconFiles.lastObject as? String, 88 | let icon = UIImage(named: lastIcon) else { 89 | return nil 90 | } 91 | 92 | return icon 93 | } 94 | 95 | @objc var version: String { 96 | return infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown" 97 | } 98 | 99 | @objc var build: String { 100 | return infoDictionary?["CFBundleVersion"] as? String ?? "Unknown" 101 | } 102 | 103 | @objc var statusBarAppearance: String { 104 | if infoDictionary?["UIViewControllerBasedStatusBarAppearance"] as? Bool == true { 105 | return "Per Controller" 106 | } else { 107 | return "Application" 108 | } 109 | } 110 | 111 | @objc var supportedOrientations: UIInterfaceOrientationMask { 112 | let application = UIApplication.shared 113 | let window = application.keyWindow 114 | return application.supportedInterfaceOrientations(for: window) 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/CALayer+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension CALayer { 26 | 27 | @objc var peek_borderColor: UIColor? { 28 | if let color = borderColor { 29 | return UIColor(cgColor: color) 30 | } 31 | 32 | return nil 33 | } 34 | 35 | @objc var peek_shadowColor: UIColor? { 36 | if let color = shadowColor { 37 | return UIColor(cgColor: color) 38 | } 39 | 40 | return nil 41 | } 42 | 43 | @objc var peek_backgroundColor: UIColor? { 44 | if let color = backgroundColor { 45 | return UIColor(cgColor: color) 46 | } 47 | 48 | return nil 49 | } 50 | 51 | open override func preparePeek(with coordinator: Coordinator) { 52 | coordinator.appendDynamic(keyPaths: [ 53 | "doubleSided", 54 | "allowsGroupOpacity", 55 | "shouldRasterize", 56 | "rasterizationScale", 57 | ], forModel: self, in: .appearance) 58 | 59 | coordinator.appendDynamic(keyPaths: [ 60 | "peek_shadowColor", 61 | "shadowOpacity", 62 | "shadowRadius", 63 | "shadowOffset", 64 | ], forModel: self, in: .shadow) 65 | 66 | coordinator.appendDynamic(keyPaths: [ 67 | "peek_borderColor", 68 | "borderWidth" 69 | ], forModel: self, in: .border) 70 | 71 | coordinator.appendDynamic(keyPaths: [ 72 | "contentsRect", 73 | "contentsCenter", 74 | "contentsScale", 75 | "contentsGravity", 76 | "geometryFlipped", 77 | "anchorPointZ", 78 | "position", 79 | "anchorPoint", 80 | "zPosition", 81 | ], forModel: self, in: .layout) 82 | 83 | var current: AnyClass = classForCoder 84 | coordinator.appendStatic(keyPath: "classForCoder", title: String(describing: current), detail: nil, value: "", in: .classes) 85 | 86 | while let next = current.superclass() { 87 | coordinator.appendStatic(keyPath: "classForCoder", title: String(describing: next), detail: nil, value: "", in: .classes) 88 | current = next 89 | } 90 | 91 | for layer in sublayers ?? [] { 92 | coordinator.appendStatic(keyPath: "layer.classForCoder", title: String(describing: layer.classForCoder), detail: "", value: layer, in: .layers) 93 | } 94 | 95 | super.preparePeek(with: coordinator) 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/NSLayoutConstraint+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension NSLayoutConstraint: PeekDescribing { 26 | 27 | internal var displayName: String { 28 | guard let ascii = perform(Selector(("asciiArtDescription"))) else { return "Unknown" } 29 | var name = "\(ascii)".components(separatedBy: ": ").last 30 | 31 | if name == "nil" { 32 | name = super.description 33 | } 34 | 35 | return name ?? super.description 36 | } 37 | 38 | @objc var peek_firstItem: String { 39 | guard let item = firstItem else { return "nil" } 40 | return "\(item.classForCoder!)" 41 | } 42 | 43 | @objc var peek_secondItem: String { 44 | guard let item = secondItem else { return "nil" } 45 | return "\(item.classForCoder!)" 46 | } 47 | 48 | open override func preparePeek(with coordinator: Coordinator) { 49 | coordinator.appendDynamic(keyPaths: [ 50 | "active", 51 | "shouldBeArchived" 52 | ], forModel: self, in: .behaviour) 53 | 54 | (coordinator as? SwiftCoordinator)? 55 | .appendEnum(keyPath: "firstAttribute", into: NSLayoutConstraint.Attribute.self, forModel: self, group: .general) 56 | 57 | coordinator.appendDynamic(keyPaths: ["peek_firstItem"], forModel: self, in: .general) 58 | 59 | (coordinator as? SwiftCoordinator)? 60 | .appendEnum(keyPath: "secondAttribute", into: NSLayoutConstraint.Attribute.self, forModel: self, group: .general) 61 | 62 | coordinator.appendDynamic(keyPaths: ["peek_secondItem"], forModel: self, in: .general) 63 | 64 | (coordinator as? SwiftCoordinator)? 65 | .appendEnum(keyPath: "relation", into: NSLayoutConstraint.Relation.self, forModel: self, group: .general) 66 | 67 | coordinator.appendDynamic(keyPaths: [ 68 | "constant", "multiplier", "priority" 69 | ], forModel: self, in: .layout) 70 | 71 | super.preparePeek(with: coordinator) 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/NSString+Peekable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Peekable.swift 3 | // Peek 4 | // 5 | // Created by Shaps Benkau on 10/03/2018. 6 | // 7 | 8 | import Foundation 9 | 10 | extension NSString { 11 | 12 | internal var peek_preview: UIImage { 13 | let textView = UITextView() 14 | 15 | textView.layoutManager.showsInvisibleCharacters = true 16 | textView.isSelectable = false 17 | textView.isEditable = false 18 | textView.isScrollEnabled = false 19 | textView.textColor = .white 20 | textView.font = UIFont.preferredFont(forTextStyle: .body) 21 | textView.backgroundColor = .clear 22 | textView.text = self as String 23 | textView.frame.size = CGSize(width: 320, height: 150) 24 | 25 | return ImageRenderer(size: textView.bounds.size).image { context in 26 | textView.drawHierarchy(in: context.format.bounds, afterScreenUpdates: true) 27 | } 28 | } 29 | 30 | open override func preparePeek(with coordinator: Coordinator) { 31 | coordinator.appendPreview(image: peek_preview, forModel: self) 32 | 33 | super.preparePeek(with: coordinator) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIActivityIndicatorView+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIActivityIndicatorView { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: ["hidesWhenStopped"], forModel: self, in: .behaviour) 29 | coordinator.appendDynamic(keyPaths: ["isAnimating"], forModel: self, in: .states) 30 | coordinator.appendDynamic(keyPaths: ["color"], forModel: self, in: .appearance) 31 | 32 | (coordinator as? SwiftCoordinator)? 33 | .appendEnum(keyPath: "activityIndicatorViewStyle", into: UIActivityIndicatorView.Style.self, forModel: self, group: .appearance) 34 | 35 | super.preparePeek(with: coordinator) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIApplication+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIApplication { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | if let image = bundle.appIcon { 29 | coordinator.appendPreview(image: image, forModel: self) 30 | } 31 | 32 | coordinator.appendDynamic(keyPaths: [ 33 | "bundle.appName", 34 | "bundle.bundleIdentifier", 35 | "bundle.version", 36 | "bundle.build", 37 | "bundle.supportedDevices", 38 | "bundle.statusBarAppearance", 39 | "statusBarFrame", 40 | "applicationIconBadgeNumber" 41 | ], forModel: self, in: .general) 42 | 43 | coordinator.appendDynamic(keyPathToName: [ 44 | ["applicationSupportsShakeToEdit": "Supports Shake to Edit"], 45 | ["ignoringInteractionEvents": "Ignoring Interactions"], 46 | ["protectedDataAvailable": "Protected Data Available"] 47 | ], forModel: self, in: .behaviour) 48 | 49 | coordinator.appendDynamic(keyPaths: [ 50 | "bundle.supportsBackgroundAudio", 51 | "bundle.supportsExternalAccessory", 52 | "bundle.supportsBackgroundFetch", 53 | "bundle.supportsLocation", 54 | "bundle.supportsNewsstand", 55 | "bundle.supportsPushNotifications", 56 | "bundle.supportsVoip", 57 | "bundle.supportsBluetooth", 58 | "bundle.supportsHealthKit", 59 | "bundle.supportsGameKit", 60 | "bundle.sharesDataViaBluetooth", 61 | ], forModel: self, in: .capabilities) 62 | 63 | super.preparePeek(with: coordinator) 64 | } 65 | 66 | @objc fileprivate var bundle: Bundle { 67 | return Bundle.main 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIBarButtonItem+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIBarButtonItem { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | var detail = "" 29 | 30 | if let model = target as? Peekable { 31 | detail = String(describing: model.classForCoder) 32 | } 33 | 34 | var title = "" 35 | 36 | if let action = action { 37 | title = String(describing: action) 38 | } 39 | 40 | coordinator.appendStatic(keyPath: title, title: title, detail: detail, value: target, in: .actions) 41 | 42 | coordinator.appendDynamic(keyPaths: [ 43 | "title", 44 | "image", 45 | "landscapeImagePhone" 46 | ], forModel: self, in: .appearance) 47 | 48 | coordinator.appendDynamic(keyPaths: [ 49 | "tag" 50 | ], forModel: self, in: .general) 51 | 52 | coordinator.appendDynamic(keyPaths: [ 53 | "imageInsets", 54 | "landscapeImagePhoneInsets" 55 | ], forModel: self, in: .layout) 56 | 57 | coordinator.appendDynamic(keyPaths: ["enabled"], forModel: self, in: .behaviour) 58 | 59 | super.preparePeek(with: coordinator) 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIColor+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIColor { 26 | 27 | @objc var peek_alpha: CGFloat { 28 | return CGFloat(Color(system: self)?.rgba.alpha ?? 0) 29 | } 30 | 31 | @objc var peek_HEX: String { 32 | if let hex = Color(system: self)?.toHex(withAlpha: false) { 33 | return "#\(hex)" 34 | } else { 35 | return "Unknown" 36 | } 37 | } 38 | 39 | @objc var peek_HSL: String { 40 | return "\(Int(hslComponents.hue * 360)), \(Int(hslComponents.saturation * 100)), \(Int(hslComponents.brightness * 100))" 41 | } 42 | 43 | @objc var peek_RGB: String { 44 | return "\(Int(rgbComponents.red * 255)), \(Int(rgbComponents.green * 255)), \(Int(rgbComponents.blue * 255))" 45 | } 46 | 47 | @available(iOS 10.0, *) 48 | @objc var colorSpace: String { 49 | let colorSpace = cgColor.colorSpace ?? CGColorSpaceCreateDeviceRGB() 50 | return colorSpace.name as String? ?? "Unknown" 51 | } 52 | 53 | open override func preparePeek(with coordinator: Coordinator) { 54 | if cgColor.pattern != nil || (self != .clear && rgbComponents.alpha != 0) { 55 | let width = UIScreen.main.nativeBounds.width / UIScreen.main.nativeScale 56 | let image = ImageRenderer(size: CGSize(width: width, height: 88)).image { context in 57 | let rect = context.format.bounds 58 | setFill() 59 | UIRectFill(rect) 60 | } 61 | 62 | coordinator.appendPreview(image: image, forModel: self) 63 | } 64 | 65 | if #available(iOS 10.0, *) { 66 | coordinator.appendDynamic(keyPaths: ["colorSpace"], forModel: self, in: .general) 67 | } 68 | 69 | guard cgColor.pattern == nil else { 70 | coordinator.appendStatic(keyPath: "cgColor.pattern", title: "Color", detail: nil, value: "Pattern", in: .general) 71 | return 72 | } 73 | 74 | guard self != .clear else { 75 | coordinator.appendStatic(keyPath: "self", title: "Color", detail: nil, value: "Clear", in: .general) 76 | return 77 | } 78 | 79 | coordinator.appendDynamic(keyPathToName: [ 80 | ["peek_HEX": "HEX"], 81 | ["peek_RGB": "RGB"], 82 | ["peek_HSL": "HSL"], 83 | ["peek_alpha": "Alpha"], 84 | ], forModel: self, in: .general) 85 | 86 | super.preparePeek(with: coordinator) 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIControl+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIControl { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: [ 29 | "enabled", "selected", "highlighted" 30 | ], forModel: self, in: .states) 31 | 32 | (coordinator as? SwiftCoordinator)? 33 | .appendEnum(keyPath: "contentVerticalAlignment", into: UIControl.ContentVerticalAlignment.self, forModel: self, group: .layout) 34 | .appendEnum(keyPath: "contentHorizontalAlignment", into: UIControl.ContentHorizontalAlignment.self, forModel: self, group: .layout) 35 | 36 | super.preparePeek(with: coordinator) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIFont+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIFont { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: [ 29 | "familyName", 30 | "fontName", 31 | "pointSize", 32 | ], forModel: self, in: .general) 33 | 34 | coordinator.appendStatic(keyPath: "fontDescriptor", title: "Font Descriptor", detail: fontDescriptor.postscriptName, value: fontDescriptor, in: .general) 35 | 36 | coordinator.appendDynamic(keyPaths: [ 37 | "descender", 38 | "capHeight", 39 | "xHeight", 40 | "lineHeight", 41 | "leading" 42 | ], forModel: self, in: .layout) 43 | 44 | super.preparePeek(with: coordinator) 45 | } 46 | 47 | @objc internal var peek_textStyle: String? { 48 | return (fontDescriptor.fontAttributes[.textStyle] as? UIFont.TextStyle)?.rawValue 49 | } 50 | 51 | } 52 | 53 | extension UIFontDescriptor { 54 | 55 | open override func preparePeek(with coordinator: Coordinator) { 56 | if let value = fontAttributes[.textStyle] as? UIFont.TextStyle { 57 | coordinator.appendStatic(keyPath: "textStyle", title: "Text Style", detail: value.rawValue, value: nil, in: .general) 58 | } 59 | 60 | if let value = fontAttributes[.family] as? String { 61 | coordinator.appendStatic(keyPath: "family", title: "Family", detail: value, value: nil, in: .general) 62 | } 63 | 64 | coordinator.appendDynamic(keyPaths: ["postscriptName"], forModel: self, in: .general) 65 | 66 | if let value = fontAttributes[.name] as? String { 67 | coordinator.appendStatic(keyPath: " name", title: "Name", detail: value, value: nil, in: .general) 68 | } 69 | 70 | if let value = fontAttributes[.face] as? String { 71 | coordinator.appendStatic(keyPath: "face", title: "Face", detail: value, value: nil, in: .general) 72 | } 73 | 74 | if let value = fontAttributes[.visibleName] as? String { 75 | coordinator.appendStatic(keyPath: "visibleName", title: "Visible Name", detail: value, value: nil, in: .general) 76 | } 77 | 78 | super.preparePeek(with: coordinator) 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIImage+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIImage { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | let preview = renderingMode != .alwaysOriginal ? withRenderingMode(.alwaysTemplate) : self 29 | coordinator.appendPreview(image: preview, forModel: self) 30 | 31 | (coordinator as? SwiftCoordinator)? 32 | .appendEnum(keyPath: "renderingMode", into: UIImage.RenderingMode.self, forModel: self, group: .appearance) 33 | .appendEnum(keyPath: "resizingMode", into: UIImage.ResizingMode.self, forModel: self, group: .appearance) 34 | .appendEnum(keyPath: "imageOrientation", into: UIImage.Orientation.self, forModel: self, group: .appearance) 35 | 36 | coordinator.appendDynamic(keyPaths: [ 37 | "scale", "size", "capInsets", "alignmentRectInsets" 38 | ], forModel: self, in: .layout) 39 | 40 | super.preparePeek(with: coordinator) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIImageView+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIImageView { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: [ 29 | "image", "highlightedImage" 30 | ], forModel: self, in: .appearance) 31 | 32 | coordinator.appendDynamic(keyPaths: [ 33 | "animationDuration", "animationRepeatCount" 34 | ], forModel: self, in: .behaviour) 35 | 36 | coordinator.appendDynamic(keyPaths: [ 37 | "isAnimating", "highlighted" 38 | ], forModel: self, in: .states) 39 | 40 | super.preparePeek(with: coordinator) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UILabel+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UILabel { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: [ 29 | "text", 30 | "attributedText", 31 | ], forModel: self, in: .typography) 32 | 33 | (coordinator as? SwiftCoordinator)? 34 | .appendEnum(keyPath: "lineBreakMode", into: NSLineBreakMode.self, forModel: self, group: .behaviour) 35 | .appendEnum(keyPath: "textAlignment", into: NSTextAlignment.self, forModel: self, group: .typography) 36 | 37 | coordinator.appendDynamic(keyPaths: ["adjustsFontSizeToFitWidth"], forModel: self, in: .behaviour) 38 | 39 | coordinator.appendDynamic(keyPaths: [ 40 | "textColor", 41 | "highlightedTextColor", 42 | "font", 43 | "minimumScaleFactor", 44 | ], forModel: self, in: .typography) 45 | 46 | coordinator.appendDynamic(keyPaths: ["preferredMaxLayoutWidth"], forModel: self, in: .layout) 47 | 48 | coordinator.appendDynamic(keyPaths: [ 49 | "numberOfLines" 50 | ], forModel: self, in: .appearance) 51 | 52 | coordinator.appendDynamic(keyPaths: [ 53 | "enabled", 54 | "highlighted" 55 | ], forModel: self, in: .states) 56 | 57 | coordinator.appendDynamic(keyPaths: [ 58 | "shadowColor", 59 | "shadowOffset" 60 | ], forModel: self, in: .shadow) 61 | 62 | super.preparePeek(with: coordinator) 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UINavigationBar+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UINavigationBar { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: [ 29 | "barTintColor", "translucent", "shadowImage" 30 | ], forModel: self, in: .appearance) 31 | 32 | (coordinator as? SwiftCoordinator)? 33 | .appendEnum(keyPath: "barStyle", into: UIBarStyle.self, forModel: self, group: .appearance) 34 | 35 | super.preparePeek(with: coordinator) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIPageControl+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIPageControl { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: [ 29 | "pageIndicatorTintColor", "currentPageIndicatorTintColor" 30 | ], forModel: self, in: .appearance) 31 | 32 | coordinator.appendDynamic(keyPaths: [ 33 | "numberOfPages", "currentPage" 34 | ], forModel: self, in: .states) 35 | 36 | coordinator.appendDynamic(keyPaths: [ 37 | "hidesForSinglePage", "defersCurrentPageDisplay" 38 | ], forModel: self, in: .behaviour) 39 | 40 | super.preparePeek(with: coordinator) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIPicker+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIPickerView { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: ["showsSelectionIndicator"], forModel: self, in: .behaviour) 29 | 30 | super.preparePeek(with: coordinator) 31 | } 32 | 33 | } 34 | 35 | extension UIDatePicker { 36 | 37 | open override func preparePeek(with coordinator: Coordinator) { 38 | coordinator.appendDynamic(keyPaths: [ 39 | "date", "minimumDate", "maximumDate", "countDownDuration", "minuteInterval" 40 | ], forModel: self, in: .appearance) 41 | 42 | (coordinator as? SwiftCoordinator)? 43 | .appendEnum(keyPath: "datePickerMode", into: UIDatePicker.Mode.self, forModel: self, group: .appearance) 44 | 45 | super.preparePeek(with: coordinator) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIProgressView+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIProgressView { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: [ 29 | "trackTintColor", "progressTintColor", 30 | "trackImage", "progressImage" 31 | ], forModel: self, in: .appearance) 32 | 33 | coordinator.appendDynamic(keyPaths: [ 34 | "progress", 35 | ], forModel: self, in: .general) 36 | 37 | super.preparePeek(with: coordinator) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIScreen+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIScreen { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: [ 29 | "wantsSoftwareDimming", 30 | "brightness" 31 | ], forModel: self, in: .appearance) 32 | 33 | coordinator.appendDynamic(keyPaths: [ 34 | "applicationFrame", 35 | "bounds", 36 | "currentMode.size", 37 | "scale", 38 | "nativeScale", 39 | "currentMode.pixelAspectRatio" 40 | ], forModel: self, in: .layout) 41 | 42 | super.preparePeek(with: coordinator) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UIScrollView+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | extension UIScrollView { 26 | 27 | open override func preparePeek(with coordinator: Coordinator) { 28 | coordinator.appendDynamic(keyPaths: [ 29 | "zoomScale", 30 | "minimumZoomScale", 31 | "maximumZoomScale", 32 | "bounces", 33 | "bouncesZoom", 34 | "alwaysBounceHorizontal", 35 | "alwaysBounceVertical", 36 | ], forModel: self, in: .appearance) 37 | 38 | (coordinator as? SwiftCoordinator)? 39 | .appendEnum(keyPath: "indicatorStyle", into: UIScrollView.IndicatorStyle.self, forModel: self, group: .appearance) 40 | .appendEnum(keyPath: "keyboardDismissMode", into: UIScrollView.KeyboardDismissMode.self, forModel: self, group: .behaviour) 41 | 42 | coordinator.appendDynamic(keyPaths: [ 43 | "contentOffset", "contentSize", "contentInset", "scrollIndicatorInsets" 44 | ], forModel: self, in: .layout) 45 | 46 | coordinator.appendDynamic(keyPaths: [ 47 | "showsHorizontalScrollIndicator", 48 | "showsVerticalScrollIndicator", 49 | "scrollEnabled", 50 | "scrollsToTop", 51 | "pagingEnabled", 52 | "decelerationRate", 53 | "directionalLockEnabled" 54 | ], forModel: self, in: .behaviour) 55 | 56 | super.preparePeek(with: coordinator) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Pod/Classes/Peekable/UISegmentedControl+Peekable.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | internal final class SegmentItem: NSObject { 26 | 27 | override var description: String { 28 | return title ?? "" 29 | } 30 | 31 | @objc var enabled: Bool = false 32 | @objc var title: String? 33 | @objc var width: CGFloat = 0 34 | @objc var image: UIImage? 35 | @objc var contentOffset: CGSize = CGSize.zero 36 | 37 | internal override var isLeaf: Bool { return false } 38 | 39 | override func preparePeek(with coordinator: Coordinator) { 40 | if let image = image { 41 | coordinator.appendPreview(image: image, forModel: self) 42 | } 43 | 44 | coordinator.appendDynamic(keyPaths: [ 45 | "title", "image" 46 | ], forModel: self, in: .appearance) 47 | 48 | coordinator.appendDynamic(keyPaths: [ 49 | "contentOffset", "width" 50 | ], forModel: self, in: .layout) 51 | 52 | coordinator.appendDynamic(keyPaths: [ 53 | "enabled" 54 | ], forModel: self, in: .states) 55 | 56 | super.preparePeek(with: coordinator) 57 | } 58 | 59 | } 60 | 61 | extension UISegmentedControl { 62 | 63 | var segments: [SegmentItem]? { 64 | var segments = [SegmentItem]() 65 | 66 | for index in 0.. Any? { 37 | guard let number = value as? NSNumber, !number.isBool() else { 38 | return nil 39 | } 40 | 41 | if number.isFloat() { 42 | return NumberTransformer.floatFormatter.string(from: number)! 43 | } 44 | 45 | return "\(number)" 46 | } 47 | 48 | override class func allowsReverseTransformation() -> Bool { 49 | return false 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Pod/Classes/Transformers/NSValue+Transformer.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | /// Creates string representations of common values, e.g. CGPoint, CGRect, etc... 26 | final class ValueTransformer: Foundation.ValueTransformer { 27 | 28 | fileprivate static var floatFormatter: NumberFormatter { 29 | let formatter = NumberFormatter() 30 | formatter.maximumFractionDigits = 1 31 | formatter.minimumFractionDigits = 0 32 | formatter.minimumIntegerDigits = 1 33 | formatter.roundingIncrement = 0.5 34 | return formatter 35 | } 36 | 37 | override func transformedValue(_ value: Any?) -> Any? { 38 | if let value = value as? NSValue { 39 | let type = String(cString: value.objCType) 40 | 41 | if type.hasPrefix("{CGRect") { 42 | let rect = value.cgRectValue 43 | return 44 | "(\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(rect.minX)))!), " + 45 | "\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(rect.minY)))!)), " + 46 | "(\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(rect.width)))!), " + 47 | "\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(rect.height)))!))" 48 | } 49 | 50 | if type.hasPrefix("{CGPoint") { 51 | let point = value.cgPointValue 52 | return "(\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(point.x)))!), \(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(point.y)))!))" 53 | } 54 | 55 | if type.hasPrefix("{UIEdgeInset") { 56 | let insets = value.uiEdgeInsetsValue 57 | return 58 | "(\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(insets.left)))!), " + 59 | "\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(insets.top)))!), " + 60 | "\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(insets.right)))!), " + 61 | "\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(insets.bottom)))!))" 62 | } 63 | 64 | if type.hasPrefix("{UIOffset") { 65 | let offset = value.uiOffsetValue 66 | return "(\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(offset.horizontal)))!), \(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(offset.vertical)))!))" 67 | } 68 | 69 | if type.hasPrefix("{CGSize") { 70 | let size = value.cgSizeValue 71 | return "(\(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(size.width)))!), \(ValueTransformer.floatFormatter.string(from: NSNumber(value: Float(size.height)))!))" 72 | } 73 | } 74 | 75 | return nil 76 | } 77 | 78 | override class func allowsReverseTransformation() -> Bool { 79 | return false 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Pod/Classes/Transformers/UIColor+Transformer.swift: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 23/04/2016 Shaps 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | import UIKit 24 | 25 | /// Creates a HEX string representation of a UIColor 26 | final class ColorTransformer: Foundation.ValueTransformer { 27 | 28 | override func transformedValue(_ value: Any?) -> Any? { 29 | if let value = value as? UIColor, let color = Color(system: value) { 30 | if color == .clear { 31 | return "Clear" 32 | } 33 | 34 | if color.cgColor.pattern != nil { 35 | return "Pattern" 36 | } 37 | 38 | return color.rgba.alpha == 0 39 | ? "Transparent" 40 | : value.peek_HEX 41 | } 42 | 43 | if CFGetTypeID(value as CFTypeRef) == CGColor.typeID { 44 | // swiftlint:disable force_cast 45 | let color = Color(cgColor: value as! CGColor) 46 | return color?.rgba.alpha == 0 ? "Clear" : color?.system.peek_HEX 47 | } 48 | 49 | return "none" 50 | } 51 | 52 | override class func allowsReverseTransformation() -> Bool { 53 | return false 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/preview.gif -------------------------------------------------------------------------------- /preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shaps80/Peek/057f0f60fc0e07c0513ac2e508d83f43be860cd3/preview.jpg -------------------------------------------------------------------------------- /pull_request_template.md: -------------------------------------------------------------------------------- 1 | **Checklist** 2 | 3 | - Latest changes from `develop` have been merged 4 | - Conflicts have been resolved 5 | - The branch is pointing to `develop` 6 | - SwiftLint hasn't reported any issues. 7 | --------------------------------------------------------------------------------