├── .codeclimate.yml ├── .gitignore ├── .jazzy.yaml ├── Example ├── NeatTipView.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── NeatTipView-Example.xcscheme ├── NeatTipView.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── NeatTipView │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── ButtonExtension.swift │ ├── ColorExtension.swift │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Icon.imageset │ │ │ ├── Contents.json │ │ │ ├── Screen Shot 2019-09-30 at 8.22.12 PM-1.png │ │ │ ├── Screen Shot 2019-09-30 at 8.22.12 PM-2.png │ │ │ └── Screen Shot 2019-09-30 at 8.22.12 PM.png │ ├── Info.plist │ ├── MutableAttributedStringExtension.swift │ ├── NeatExampleViewController.swift │ ├── Roboto-Black.ttf │ ├── Roboto-Bold.ttf │ ├── Roboto-Italic.ttf │ ├── Roboto-Light.ttf │ ├── Roboto-Medium.ttf │ ├── Roboto-Regular.ttf │ ├── Roboto-Thin.ttf │ └── ViewExtension.swift ├── Podfile ├── Podfile.lock ├── Tests │ ├── Info.plist │ └── Tests.swift └── build │ └── XCBuildData │ ├── 8834efa90c1951fc93e2fffd39c48312-desc.xcbuild │ ├── 8834efa90c1951fc93e2fffd39c48312-manifest.xcbuild │ └── BuildDescriptionCacheIndex-3e874781e1f9d13bab0b356a0ec47683 ├── LICENSE ├── NeatTipView.podspec ├── NeatTipView ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── Extensions │ └── AttributedStringExtension.swift │ ├── Preferences │ └── NeatViewPreferences.swift │ └── View │ ├── ArrowPosition.swift │ ├── NeatArrowPath.swift │ ├── NeatBubbleView.swift │ ├── NeatTipView.swift │ ├── NeatTipViewConstraintsInitialization.swift │ └── NeatTipViewTipHelpers.swift ├── NeatTipViewLogo.png ├── README.md ├── _Pods.xcodeproj ├── docs ├── Classes.html ├── Classes │ ├── NeatBubbleView.html │ └── NeatTipView.html ├── Enums.html ├── Enums │ ├── AppearanceAnimationType.html │ └── ArrowPosition.html ├── Structs.html ├── Structs │ ├── NeatAnimationPreferences.html │ ├── NeatBubbleStylePreferences.html │ ├── NeatLayoutPreferences.html │ ├── NeatOverlayStylePreferences.html │ └── NeatViewPreferences.html ├── badge.svg ├── css │ ├── highlight.css │ └── jazzy.css ├── docsets │ ├── NeatTipView.docset │ │ └── Contents │ │ │ ├── Info.plist │ │ │ └── Resources │ │ │ ├── Documents │ │ │ ├── Classes.html │ │ │ ├── Classes │ │ │ │ ├── NeatBubbleView.html │ │ │ │ └── NeatTipView.html │ │ │ ├── Enums.html │ │ │ ├── Enums │ │ │ │ ├── AppearanceAnimationType.html │ │ │ │ └── ArrowPosition.html │ │ │ ├── Structs.html │ │ │ ├── Structs │ │ │ │ ├── NeatAnimationPreferences.html │ │ │ │ ├── NeatBubbleStylePreferences.html │ │ │ │ ├── NeatLayoutPreferences.html │ │ │ │ ├── NeatOverlayStylePreferences.html │ │ │ │ └── NeatViewPreferences.html │ │ │ ├── badge.svg │ │ │ ├── css │ │ │ │ ├── highlight.css │ │ │ │ └── jazzy.css │ │ │ ├── img │ │ │ │ ├── DisappearanceAnimationType.html │ │ │ │ ├── carat.png │ │ │ │ ├── dash.png │ │ │ │ └── gh.png │ │ │ ├── index.html │ │ │ ├── js │ │ │ │ ├── jazzy.js │ │ │ │ └── jquery.min.js │ │ │ ├── search.json │ │ │ └── undocumented.json │ │ │ └── docSet.dsidx │ └── NeatTipView.tgz ├── img │ ├── DisappearanceAnimationType.html │ ├── carat.png │ ├── dash.png │ └── gh.png ├── index.html ├── js │ ├── jazzy.js │ └── jquery.min.js ├── search.json └── undocumented.json ├── generate-docs.sh └── tipViewsExample.gif /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | version: "2" # required to adjust maintainability checks 2 | checks: 3 | argument-count: 4 | config: 5 | threshold: 8 6 | complex-logic: 7 | config: 8 | threshold: 5 9 | file-lines: 10 | config: 11 | threshold: 500 12 | method-complexity: 13 | config: 14 | threshold: 5 15 | method-count: 16 | config: 17 | threshold: 20 18 | method-lines: 19 | config: 20 | threshold: 25 21 | nested-control-flow: 22 | config: 23 | threshold: 3 24 | return-statements: 25 | enabled: false 26 | similar-code: 27 | config: 28 | threshold: # language-specific defaults. an override will affect all languages. 29 | identical-code: 30 | config: 31 | threshold: # language-specific defaults. an override will affect all languages. 32 | plugins: 33 | swiftlint: 34 | enabled: true 35 | 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | # Swift Package Manager 31 | # 32 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 33 | # Packages/ 34 | .build/ 35 | 36 | # CocoaPods 37 | # 38 | # We recommend against adding the Pods directory to your .gitignore. However 39 | # you should judge for yourself, the pros and cons are mentioned at: 40 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 41 | # 42 | Example/Pods/ 43 | 44 | # Carthage 45 | # 46 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 47 | # Carthage/Checkouts 48 | 49 | Carthage/Build 50 | 51 | **/.DS_Store 52 | 53 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | exclude: 2 | - Example/* 3 | module: NeatTipView 4 | podspec: NeatTipView.podspec 5 | -------------------------------------------------------------------------------- /Example/NeatTipView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/NeatTipView.xcodeproj/xcshareddata/xcschemes/NeatTipView-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 65 | 66 | 67 | 68 | 78 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Example/NeatTipView.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/NeatTipView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/NeatTipView.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/NeatTipView/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // NeatTipView 4 | // 5 | // Created by german.stabile@gmail.com on 07/19/2019. 6 | // Copyright (c) 2019 german.stabile@gmail.com. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Example/NeatTipView/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/NeatTipView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Roboto-Regular 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 37 | 45 | 53 | 61 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/NeatTipView/ButtonExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ButtonExtension.swift 3 | // NeatTipView_Example 4 | // 5 | // Created by sol manini on 10/1/19. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIButton { 12 | func configure( 13 | bkgColor: UIColor = .aquamarineGreen, 14 | borderColor: UIColor = .aquamarineGreen, 15 | cornerRadius: CGFloat = 5, 16 | borderWidth: CGFloat = 1, 17 | horizontalPadding: CGFloat = 15, 18 | verticalPadding: CGFloat = 10, 19 | titleColor: UIColor = .white 20 | ) { 21 | backgroundColor = bkgColor 22 | layer.cornerRadius = cornerRadius 23 | layer.borderColor = borderColor.cgColor 24 | layer.borderWidth = borderWidth 25 | contentEdgeInsets = UIEdgeInsets( 26 | top: verticalPadding, 27 | left: horizontalPadding, 28 | bottom: verticalPadding, 29 | right: horizontalPadding 30 | ) 31 | setTitleColor(titleColor, for: .normal) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Example/NeatTipView/ColorExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorExtension.swift 3 | // NeatTipView_Example 4 | // 5 | // Created by Germán Stábile on 9/27/19. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | 13 | static let greeny = UIColor(red: 38, green: 208, blue: 124) 14 | static let purply = UIColor(red: 77, green: 73, blue: 190) 15 | static let altoGray = UIColor(red: 216, green: 216, blue: 216) 16 | static let cadetBlue = UIColor(red: 167, green: 175, blue: 199) 17 | static let paleRose = UIColor(red: 255, green: 225, blue: 242) 18 | static let gold = UIColor(red: 255, green: 215, blue: 0) 19 | 20 | static let aquamarineGreen = UIColor(red: 112, green: 255, blue: 176) 21 | static let heliotropePurple = UIColor(red: 212, green: 115, blue: 255) 22 | static let anakiwaBlue = UIColor(red: 112, green: 237, blue: 255) 23 | static let sharkBlack = UIColor(red: 38, green: 40, blue: 43) 24 | 25 | convenience init(red: Int, green: Int, blue: Int) { 26 | self.init(red: min(CGFloat(red), 255.0) / 255.0, 27 | green: min(CGFloat(green), 255.0) / 255.0, 28 | blue: min(CGFloat(blue), 255.0) / 255.0, 29 | alpha: 1.0) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Example/NeatTipView/Images.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 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/NeatTipView/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/NeatTipView/Images.xcassets/Icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Screen Shot 2019-09-30 at 8.22.12 PM.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Screen Shot 2019-09-30 at 8.22.12 PM-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Screen Shot 2019-09-30 at 8.22.12 PM-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Example/NeatTipView/Images.xcassets/Icon.imageset/Screen Shot 2019-09-30 at 8.22.12 PM-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Images.xcassets/Icon.imageset/Screen Shot 2019-09-30 at 8.22.12 PM-1.png -------------------------------------------------------------------------------- /Example/NeatTipView/Images.xcassets/Icon.imageset/Screen Shot 2019-09-30 at 8.22.12 PM-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Images.xcassets/Icon.imageset/Screen Shot 2019-09-30 at 8.22.12 PM-2.png -------------------------------------------------------------------------------- /Example/NeatTipView/Images.xcassets/Icon.imageset/Screen Shot 2019-09-30 at 8.22.12 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Images.xcassets/Icon.imageset/Screen Shot 2019-09-30 at 8.22.12 PM.png -------------------------------------------------------------------------------- /Example/NeatTipView/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 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 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/NeatTipView/MutableAttributedStringExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MutableAttributedStringExtension.swift 3 | // NeatTipView_Example 4 | // 5 | // Created by Germán Stábile on 7/19/19. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension NSMutableAttributedString { 12 | 13 | func highlight(text: String, with attributes: [NSAttributedString.Key: Any]) { 14 | let range = (string as NSString).range(of: text) 15 | setAttributes(attributes, 16 | range: range) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example/NeatTipView/NeatExampleViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeatExampleViewController.swift 3 | // NeatTipView_Example 4 | // 5 | // Created by Germán Stábile on 7/19/19. 6 | // Copyright © 2019 Rootstrap. All rights reserved. 7 | // 8 | 9 | import NeatTipView 10 | import UIKit 11 | 12 | class NeatExampleViewController: UIViewController { 13 | 14 | @IBOutlet weak var fromLeftButton: UIButton! 15 | @IBOutlet weak var fromRightButton: UIButton! 16 | @IBOutlet weak var fromTopButton: UIButton! 17 | @IBOutlet weak var fromBottomButton: UIButton! 18 | @IBOutlet weak var attachButton: UIButton! 19 | 20 | override func viewDidLoad() { 21 | fromLeftButton.configure(titleColor: .sharkBlack) 22 | fromRightButton.configure( 23 | bkgColor: .heliotropePurple, 24 | borderColor: .heliotropePurple 25 | ) 26 | fromTopButton.configure( 27 | bkgColor: .anakiwaBlue, 28 | borderColor: .anakiwaBlue, 29 | titleColor: .sharkBlack 30 | ) 31 | fromBottomButton.configure( 32 | bkgColor: .white, 33 | titleColor: .aquamarineGreen 34 | ) 35 | attachButton.configure( 36 | bkgColor: .white, 37 | borderColor: .heliotropePurple, 38 | titleColor: .heliotropePurple 39 | ) 40 | 41 | fromLeftButton.addShadow() 42 | fromRightButton.addShadow() 43 | fromTopButton.addShadow() 44 | } 45 | 46 | @IBAction func fromLeftButtonTapped(sender: UIButton) { 47 | var preferences = NeatViewPreferences() 48 | preferences.animationPreferences.appearanceAnimationType = .fromLeft 49 | preferences.animationPreferences.disappearanceAnimationType = .toLeft 50 | showTipView(with: preferences, 51 | center: CGPoint(x: sender.frame.minX, y: sender.frame.midY), 52 | arrowPosition: .right) 53 | } 54 | 55 | @IBAction func fromRightButtonTapped(sender: UIButton) { 56 | var preferences = NeatViewPreferences() 57 | preferences.animationPreferences.appearanceAnimationType = .fromRight 58 | preferences.animationPreferences.disappearanceAnimationType = .toRight 59 | preferences.bubbleStylePreferences.backgroundColor = .greeny 60 | preferences.bubbleStylePreferences.borderColor = .cadetBlue 61 | showTipView(with: preferences, 62 | center: CGPoint(x: sender.frame.maxX, y: sender.frame.midY), 63 | arrowPosition: .left, 64 | attributedText: neatAttributedString()) 65 | } 66 | 67 | @IBAction func fromBottomButtonTapped(sender: UIButton) { 68 | var preferences = NeatViewPreferences() 69 | preferences.animationPreferences.appearanceAnimationType = .fromBottom 70 | preferences.animationPreferences.disappearanceAnimationType = .toBottom 71 | showTipView(with: preferences, 72 | center: CGPoint(x: sender.frame.midX, 73 | y: sender.frame.maxY), 74 | arrowPosition: .top) 75 | } 76 | 77 | @IBAction func fromTopButtonTapped(sender: UIButton) { 78 | var preferences = NeatViewPreferences() 79 | preferences.animationPreferences.appearanceAnimationType = .fromTop 80 | preferences.animationPreferences.disappearanceAnimationType = .toTop 81 | preferences.bubbleStylePreferences.backgroundColor = .purply 82 | showTipView(with: preferences, 83 | center: CGPoint(x: sender.frame.midX, 84 | y: sender.frame.minY), 85 | arrowPosition: .bottom, 86 | attributedText: neatAttributedString()) 87 | } 88 | 89 | @IBAction func attachToButtonTapped(_ sender: UIButton) { 90 | var preferences = NeatViewPreferences() 91 | preferences.bubbleStylePreferences.backgroundColor = UIColor.paleRose 92 | NeatTipView.attach(to: sender, in: view, 93 | with: diversityAttributedString(), 94 | preferences: preferences, 95 | arrowPosition: .top) 96 | } 97 | 98 | func showTipView(with preferences: NeatViewPreferences, 99 | center: CGPoint, 100 | arrowPosition: ArrowPosition, 101 | attributedText: NSAttributedString? = nil) { 102 | let tipView = NeatTipView(superview: view, 103 | centerPoint: center, 104 | attributedString: attributedText ?? attributedString(), 105 | preferences: preferences, 106 | arrowPosition: arrowPosition) 107 | tipView.show() 108 | } 109 | 110 | func attributedString() -> NSAttributedString { 111 | let attributedString = 112 | NSMutableAttributedString(string: "This is an example of one of the neat tips you can create with NeatTipView", 113 | attributes: [.font: UIFont.systemFont(ofSize: 15)]) 114 | attributedString.highlight(text: "NeatTipView", 115 | with: [.font: UIFont.systemFont(ofSize: 15, 116 | weight: .bold)]) 117 | 118 | return attributedString 119 | } 120 | 121 | func diversityAttributedString() -> NSAttributedString { 122 | let coloredWords: [(UIColor, String)] = 123 | [(UIColor.red, "This "), (UIColor.orange, "is a "), 124 | (UIColor.gold, "diverse "), (UIColor.green, "Neat "), 125 | (UIColor.blue, "Tip "), (UIColor.purple, "View ")] 126 | 127 | let attributedString = NSMutableAttributedString() 128 | 129 | coloredWords.forEach { 130 | attributedString.append( 131 | NSAttributedString(string: $0.1, 132 | attributes: [.strokeColor: UIColor.black, 133 | .foregroundColor: $0.0, 134 | .font: UIFont.systemFont(ofSize: 14, weight: .bold)]) 135 | ) 136 | } 137 | 138 | return attributedString 139 | } 140 | 141 | func neatAttributedString() -> NSAttributedString { 142 | let paragraph = NSMutableParagraphStyle() 143 | paragraph.alignment = .center 144 | paragraph.lineHeightMultiple = 0.8 145 | 146 | let attributedString = NSMutableAttributedString( 147 | string: "100", 148 | attributes: [ 149 | .font: UIFont.systemFont(ofSize: 26, weight: .semibold), 150 | .foregroundColor: UIColor.white, 151 | .paragraphStyle: paragraph] 152 | ) 153 | attributedString.append( 154 | NSAttributedString( 155 | string: "%\n", 156 | attributes: [ 157 | .font: UIFont.systemFont(ofSize: 14, 158 | weight: .medium), 159 | .foregroundColor: UIColor.white, 160 | .baselineOffset: 8 161 | ] 162 | ) 163 | ) 164 | attributedString.append( 165 | NSAttributedString( 166 | string: "NEAT TIPS", 167 | attributes: [ 168 | .font: UIFont.systemFont(ofSize: 11, weight: .semibold), 169 | .foregroundColor: UIColor.white, 170 | .paragraphStyle: paragraph 171 | ] 172 | ) 173 | ) 174 | 175 | return attributedString 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Example/NeatTipView/Roboto-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Roboto-Black.ttf -------------------------------------------------------------------------------- /Example/NeatTipView/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Roboto-Bold.ttf -------------------------------------------------------------------------------- /Example/NeatTipView/Roboto-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Roboto-Italic.ttf -------------------------------------------------------------------------------- /Example/NeatTipView/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Roboto-Light.ttf -------------------------------------------------------------------------------- /Example/NeatTipView/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Roboto-Medium.ttf -------------------------------------------------------------------------------- /Example/NeatTipView/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Roboto-Regular.ttf -------------------------------------------------------------------------------- /Example/NeatTipView/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/NeatTipView/Roboto-Thin.ttf -------------------------------------------------------------------------------- /Example/NeatTipView/ViewExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewExtension.swift 3 | // NeatTipView_Example 4 | // 5 | // Created by sol manini on 10/2/19. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | func addShadow( 13 | color: UIColor = .gray, 14 | offset: CGSize = CGSize(width: 1, height: 1), 15 | opacity: Float = 0.3, 16 | radius: CGFloat = 5 17 | ) { 18 | layer.shadowRadius = radius 19 | layer.shadowColor = color.cgColor 20 | layer.shadowOffset = offset 21 | layer.shadowOpacity = opacity 22 | layer.masksToBounds = false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'NeatTipView_Example' do 4 | pod 'NeatTipView', :path => '../' 5 | 6 | target 'NeatTipView_Tests' do 7 | inherit! :search_paths 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - NeatTipView (1.0.1) 3 | 4 | DEPENDENCIES: 5 | - NeatTipView (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | NeatTipView: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | NeatTipView: e1a80fcea835e74a0ed3fb260b1f62a907013e78 13 | 14 | PODFILE CHECKSUM: 7896c3e107576f12b6c365708e391e5976da644d 15 | 16 | COCOAPODS: 1.7.5 17 | -------------------------------------------------------------------------------- /Example/Tests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import NeatTipView 3 | 4 | class Tests: XCTestCase { 5 | 6 | override func setUp() { 7 | super.setUp() 8 | // Put setup code here. This method is called before the invocation of each test method in the class. 9 | } 10 | 11 | override func tearDown() { 12 | // Put teardown code here. This method is called after the invocation of each test method in the class. 13 | super.tearDown() 14 | } 15 | 16 | func testExample() { 17 | // This is an example of a functional test case. 18 | XCTAssert(true, "Pass") 19 | } 20 | 21 | func testPerformanceExample() { 22 | // This is an example of a performance test case. 23 | self.measure() { 24 | // Put the code you want to measure the time of here. 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Example/build/XCBuildData/8834efa90c1951fc93e2fffd39c48312-desc.xcbuild: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/build/XCBuildData/8834efa90c1951fc93e2fffd39c48312-desc.xcbuild -------------------------------------------------------------------------------- /Example/build/XCBuildData/BuildDescriptionCacheIndex-3e874781e1f9d13bab0b356a0ec47683: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/Example/build/XCBuildData/BuildDescriptionCacheIndex-3e874781e1f9d13bab0b356a0ec47683 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 german.stabile@gmail.com 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 | -------------------------------------------------------------------------------- /NeatTipView.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint NeatTipView.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'NeatTipView' 11 | s.version = '1.0.1' 12 | s.summary = 'Display tooltip views in swift.' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | NeatTipView allows you to display message tooltips that can be used as call to actions or informative tips. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/rootstrap/NeatTipView' 25 | 26 | s.license = { :type => 'MIT', :file => 'LICENSE' } 27 | s.author = { 'german.stabile@gmail.com' => 'german.stabile@gmail.com' } 28 | s.source = { :git => 'https://github.com/rootstrap/NeatTipView.git', :tag => s.version.to_s } 29 | 30 | s.ios.deployment_target = '11.0' 31 | 32 | s.source_files = 'NeatTipView/Classes/**/*' 33 | 34 | s.frameworks = 'UIKit' 35 | 36 | s.swift_version = '5.0' 37 | end 38 | -------------------------------------------------------------------------------- /NeatTipView/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/NeatTipView/Assets/.gitkeep -------------------------------------------------------------------------------- /NeatTipView/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/NeatTipView/Classes/.gitkeep -------------------------------------------------------------------------------- /NeatTipView/Classes/Extensions/AttributedStringExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AttributedStringExtension.swift 3 | // NeatTipView 4 | // 5 | // Created by Germán Stábile on 7/24/19. 6 | // 7 | 8 | import Foundation 9 | 10 | extension NSAttributedString { 11 | func size(with size: CGSize) -> CGSize { 12 | let framesetter = CTFramesetterCreateWithAttributedString(self) 13 | return CTFramesetterSuggestFrameSizeWithConstraints(framesetter, 14 | CFRange(location: 0,length: 0), 15 | nil, 16 | size, 17 | nil) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /NeatTipView/Classes/Preferences/NeatViewPreferences.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeatViewPreferences.swift 3 | // NeatTipView 4 | // 5 | // Created by Germán Stábile on 7/19/19. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | NeatViewPreferences contains all the configurable properties for NeatTipView. 12 | Each property manages one aspect of the configuration properties: 13 | - `layoutPreferences`: Insets and arrow size. 14 | - `animationPreferences`: UIView animation timing, curves, etc. 15 | - `bubbleStylePreferences`: The visual style of the bubble itself. 16 | - `overlayStylePreferences`: The visual style of the background overlay. 17 | Each property comes with default values for easy usage. 18 | */ 19 | public struct NeatViewPreferences { 20 | public var layoutPreferences = NeatLayoutPreferences() 21 | public var animationPreferences = NeatAnimationPreferences() 22 | public var bubbleStylePreferences = NeatBubbleStylePreferences() 23 | public var overlayStylePreferences = NeatOverlayStylePreferences() 24 | 25 | public init() { } 26 | } 27 | 28 | /// Bubble view style properties 29 | public struct NeatBubbleStylePreferences { 30 | public var backgroundColor: UIColor = .white 31 | public var borderWidth: CGFloat = 3 32 | public var borderColor: UIColor = .lightGray 33 | public var cornerRadius: CGFloat = 8 34 | public var shadowColor: UIColor = .black 35 | public var shadowOffset = CGSize(width: 0, height: 5) 36 | public var shadowRadius: CGFloat = 5 37 | public var shadowOpacity: Float = 0.25 38 | } 39 | 40 | /// Overlay style properties 41 | public struct NeatOverlayStylePreferences { 42 | public var backgroundColor: UIColor = UIColor.black.withAlphaComponent(0.55) 43 | } 44 | 45 | /** 46 | AppearanceAnimationType defines all available appearance animations, 47 | this will define how the View is presented each time `.show()` is called 48 | */ 49 | public enum AppearanceAnimationType { 50 | case fromTop 51 | case fromBottom 52 | case fromLeft 53 | case fromRight 54 | case fadeIn 55 | } 56 | 57 | /** 58 | AppearanceAnimationType defines all available dismiss animations, 59 | this will define how the View is presented each time `.dismiss()` is called 60 | */ 61 | public enum DisappearanceAnimationType { 62 | case toTop 63 | case toBottom 64 | case toLeft 65 | case toRight 66 | case fadeOut 67 | } 68 | 69 | /// View layout properties 70 | public struct NeatLayoutPreferences { 71 | public var horizontalInsets: CGFloat = 20 72 | public var verticalInsets: CGFloat = 20 73 | public var contentHorizontalInsets: CGFloat = 10 74 | public var minWidth: CGFloat = 30 75 | public var contentVerticalInsets: CGFloat = 10 76 | public var arrowWidth: CGFloat = 15 77 | public var arrowHeight: CGFloat = 10 78 | 79 | public init() { } 80 | } 81 | 82 | /// Animation preferences, applies both for appearance and dissapearance 83 | public struct NeatAnimationPreferences { 84 | public var animationDuration: TimeInterval = 0.3 85 | public var animationCurve: UIView.AnimationOptions = .curveEaseInOut 86 | public var animationOffset: CGFloat = Constants.animationOffset 87 | public var appearanceAnimationType: AppearanceAnimationType = .fromBottom 88 | public var disappearanceAnimationType: DisappearanceAnimationType = .toBottom 89 | 90 | public init() { } 91 | } 92 | -------------------------------------------------------------------------------- /NeatTipView/Classes/View/ArrowPosition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ArrowPosition.swift 3 | // NeatTipView 4 | // 5 | // Created by Mauricio Cousillas on 8/23/19. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | ArrowPosition defines the available orientations of the view's tip. 12 | Each case defines where the tip is "pointing" to (so `top` will point upwards for example) 13 | */ 14 | public enum ArrowPosition { 15 | case top 16 | case bottom 17 | case left 18 | case right 19 | case any 20 | 21 | // MARK: Helpers 22 | var isVerticalArrow: Bool { 23 | return self == .top || self == .bottom 24 | } 25 | /// Returns the position counter part 26 | var counterpart: ArrowPosition { 27 | switch self { 28 | case .left: 29 | return .right 30 | case .right: 31 | return .left 32 | case .top: 33 | return .bottom 34 | case .bottom: 35 | return .top 36 | default: 37 | return .any 38 | } 39 | } 40 | 41 | /// Returns the available positions for the vertical axis 42 | static var verticalPositions: [ArrowPosition] { 43 | return [.top, .bottom] 44 | } 45 | 46 | /// Returns the available positions for the horizontal axis 47 | static var horizontalPositions: [ArrowPosition] { 48 | return [.left, .right] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /NeatTipView/Classes/View/NeatArrowPath.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeatArrowView.swift 3 | // NeatTipView 4 | // 5 | // Created by Germán Stábile on 7/17/19. 6 | // Copyright © 2019 TopTier labs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /** 12 | NeatArrowPath helper used to store and calculate the actual arrow path. 13 | */ 14 | struct NeatArrowPath { 15 | /// Minimum distance to the edge of the frame so the arrow does not clip the view. 16 | let minBounds: CGFloat = 8 17 | 18 | /// The arrow position. 19 | let position: ArrowPosition 20 | 21 | /// The center point used to start drawing. 22 | let center: CGPoint 23 | 24 | /// The width of the arrow. 25 | let width: CGFloat 26 | 27 | /// The height of the arrow 28 | let height: CGFloat 29 | 30 | /// The containing frame where the arrow will be drawn. 31 | let frame: CGRect 32 | 33 | /// The offset created by stroking a border (needed to offset the path so it merges with the bubble) 34 | let borderOffset: CGFloat 35 | 36 | /** 37 | Returns an instance of UIBezierPath that has the shape and position of the arrow. 38 | */ 39 | var path: UIBezierPath { 40 | let path = UIBezierPath() 41 | 42 | drawingPoints.enumerated().forEach { index, point in 43 | if index == 0 { 44 | path.move(to: point) 45 | } else { 46 | path.addLine(to: point) 47 | } 48 | } 49 | path.close() 50 | 51 | return path 52 | } 53 | 54 | /// Returns the drawing point for a specific position 55 | var drawingPoints: [CGPoint] { 56 | switch position { 57 | case .top: 58 | return topDrawingPoints 59 | case .left: 60 | return leftDrawingPoints 61 | case .bottom, .any: 62 | return bottomDrawingPoints 63 | case .right: 64 | return rightDrawingPoints 65 | } 66 | } 67 | 68 | var topDrawingPoints: [CGPoint] { 69 | let initialX = max(center.x - frame.origin.x - (width / 2), minBounds) 70 | let midX = center.x - frame.origin.x 71 | let finalX = midX + (width / 2) 72 | let initialY = height + borderOffset 73 | return [CGPoint(x: initialX, y: initialY), 74 | CGPoint(x: midX, y: 0), 75 | CGPoint(x: finalX, y: initialY)] 76 | } 77 | 78 | var bottomDrawingPoints: [CGPoint] { 79 | let initialX = max(center.x - frame.origin.x - (width / 2), minBounds) 80 | let midX = initialX + (width / 2) 81 | let finalX = initialX + width 82 | let endY = frame.height - height - borderOffset 83 | return [CGPoint(x: initialX, y: endY), 84 | CGPoint(x: midX, y: frame.height), 85 | CGPoint(x: finalX, y: endY)] 86 | } 87 | 88 | var leftDrawingPoints: [CGPoint] { 89 | let initialY = max(center.y - (width / 2) - frame.origin.y, minBounds) 90 | let midY = initialY + (width / 2) 91 | let finalY = initialY + width 92 | let initialX = height + borderOffset 93 | return [CGPoint(x: initialX, y: initialY), 94 | CGPoint(x: 0, y: midY), 95 | CGPoint(x: initialX, y: finalY)] 96 | } 97 | 98 | var rightDrawingPoints: [CGPoint] { 99 | let initialY = max(center.y - (width / 2) - frame.origin.y, minBounds) 100 | let midY = initialY + (width / 2) 101 | let finalY = initialY + width 102 | let endX = frame.width - height - borderOffset 103 | return [CGPoint(x: endX, y: initialY), 104 | CGPoint(x: frame.width, y: midY), 105 | CGPoint(x: endX, y: finalY)] 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /NeatTipView/Classes/View/NeatBubbleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BubbleView.swift 3 | // NeatTipView 4 | // 5 | // Created by Mauricio Cousillas on 8/23/19. 6 | // 7 | 8 | import UIKit 9 | 10 | /** 11 | NeatBubbleView renders the actual tooltip using the defined preferences and string. 12 | You can use this view without depending on `NeatTipView` if you need something custom, 13 | or you don't want a full-screen overlay. 14 | */ 15 | public class NeatBubbleView: UIView { 16 | /// Configuration preferences. 17 | public var preferences: NeatViewPreferences 18 | /// The string to be displayed 19 | public var attributedString: NSAttributedString 20 | /// Active constraints for the content of the bubble. 21 | private var activeConstraints: [NSLayoutConstraint] = [] 22 | 23 | var layoutPreferences: NeatLayoutPreferences { 24 | return preferences.layoutPreferences 25 | } 26 | 27 | var stylePreferences: NeatBubbleStylePreferences { 28 | return preferences.bubbleStylePreferences 29 | } 30 | 31 | /// Point used to center the tip of the bubble's arrow. Redraws if changed. 32 | public var centerPoint: CGPoint { 33 | didSet { 34 | setNeedsDisplay() 35 | layoutIfNeeded() 36 | } 37 | } 38 | 39 | /// Arrow's position. Re-calculates layout constraints if changed. 40 | public var arrowPosition: ArrowPosition { 41 | didSet { 42 | clearConstraints() 43 | activateConstraints() 44 | setNeedsDisplay() 45 | layoutIfNeeded() 46 | } 47 | } 48 | 49 | /// Wrapper view used to contain the actual content displayed by the user. 50 | lazy var contentContainerView: UIView = { 51 | let view = UIView() 52 | view.translatesAutoresizingMaskIntoConstraints = false 53 | view.backgroundColor = .clear 54 | view.layer.masksToBounds = false 55 | 56 | return view 57 | }() 58 | 59 | /// Label used to render the actual attributed string. 60 | lazy var label: UILabel = { 61 | let label = UILabel() 62 | label.numberOfLines = 0 63 | label.lineBreakMode = .byWordWrapping 64 | label.translatesAutoresizingMaskIntoConstraints = false 65 | label.attributedText = attributedString 66 | 67 | return label 68 | }() 69 | 70 | /// Programatic view initialization 71 | public init( 72 | with centerPoint: CGPoint, 73 | message attributedString: NSAttributedString, 74 | preferences: NeatViewPreferences = NeatViewPreferences(), 75 | arrowPosition: ArrowPosition = .top 76 | ) { 77 | self.centerPoint = centerPoint 78 | self.attributedString = attributedString 79 | self.preferences = preferences 80 | self.arrowPosition = arrowPosition == .any ? .top : arrowPosition 81 | self.preferences = preferences 82 | super.init(frame: .zero) 83 | 84 | translatesAutoresizingMaskIntoConstraints = false 85 | backgroundColor = .clear 86 | setupViews() 87 | } 88 | 89 | /// View intialization called when using xibs with sane defaults. 90 | required init?(coder aDecoder: NSCoder) { 91 | arrowPosition = .bottom 92 | preferences = NeatViewPreferences() 93 | arrowPosition = .top 94 | centerPoint = .zero 95 | attributedString = NSAttributedString(string: "") 96 | super.init(coder: aDecoder) 97 | setupViews() 98 | } 99 | 100 | override public func draw(_ rect: CGRect) { 101 | super.draw(rect) 102 | 103 | layer.shadowColor = stylePreferences.shadowColor.cgColor 104 | layer.shadowOffset = stylePreferences.shadowOffset 105 | layer.shadowRadius = stylePreferences.shadowRadius 106 | layer.shadowOpacity = stylePreferences.shadowOpacity 107 | 108 | layer.masksToBounds = false 109 | 110 | let arrowPath = NeatArrowPath( 111 | position: arrowPosition, 112 | center: centerPoint, 113 | width: layoutPreferences.arrowWidth, 114 | height: layoutPreferences.arrowHeight, 115 | frame: frame, 116 | borderOffset: stylePreferences.borderWidth 117 | ).path 118 | 119 | let path = UIBezierPath( 120 | roundedRect: contentContainerView.frame.insetBy( 121 | dx: stylePreferences.borderWidth, 122 | dy: stylePreferences.borderWidth 123 | ), 124 | cornerRadius: preferences.bubbleStylePreferences.cornerRadius 125 | ) 126 | 127 | stylePreferences.backgroundColor.setFill() 128 | stylePreferences.borderColor.setStroke() 129 | path.lineWidth = stylePreferences.borderWidth 130 | 131 | path.append(arrowPath) 132 | 133 | path.close() 134 | path.stroke() 135 | path.fill() 136 | } 137 | 138 | /// View setup. 139 | private func setupViews() { 140 | addSubview(contentContainerView) 141 | contentContainerView.addSubview(label) 142 | activateConstraints() 143 | } 144 | 145 | /// Constraints setup. 146 | private func activateConstraints() { 147 | activeConstraints = [ 148 | contentContainerView.topAnchor.constraint(equalTo: topAnchor, constant: arrowPosition == .top ? layoutPreferences.arrowHeight : 0), 149 | contentContainerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: arrowPosition == .bottom ? -layoutPreferences.arrowHeight : 0), 150 | contentContainerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: arrowPosition == .left ? layoutPreferences.arrowHeight : 0), 151 | contentContainerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: arrowPosition == .right ? -layoutPreferences.arrowHeight : 0), 152 | contentContainerView.widthAnchor.constraint(greaterThanOrEqualToConstant: layoutPreferences.arrowWidth + layoutPreferences.arrowHeight), 153 | contentContainerView.heightAnchor.constraint(greaterThanOrEqualToConstant: layoutPreferences.arrowWidth + layoutPreferences.arrowHeight), 154 | label.leadingAnchor.constraint(equalTo: contentContainerView.leadingAnchor, 155 | constant: layoutPreferences.contentHorizontalInsets), 156 | label.trailingAnchor.constraint(equalTo: contentContainerView.trailingAnchor, 157 | constant: -layoutPreferences.contentHorizontalInsets), 158 | label.topAnchor.constraint(equalTo: contentContainerView.topAnchor, 159 | constant: layoutPreferences.verticalInsets), 160 | label.bottomAnchor.constraint(equalTo: contentContainerView.bottomAnchor, 161 | constant: -layoutPreferences.verticalInsets) 162 | ] 163 | 164 | NSLayoutConstraint.activate(activeConstraints) 165 | } 166 | 167 | /// Clears constraints if needed. 168 | private func clearConstraints() { 169 | NSLayoutConstraint.deactivate(activeConstraints) 170 | activeConstraints = [] 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /NeatTipView/Classes/View/NeatTipView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeatTipView.swift 3 | // NeatTipView 4 | // 5 | // Created by Germán Stábile on 7/17/19. 6 | // Copyright © 2019 TopTier labs. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Constants { 12 | static let screenWidth = UIScreen.main.bounds.width 13 | static let screenHeight = UIScreen.main.bounds.height 14 | static let animationOffset: CGFloat = 200 15 | } 16 | 17 | /** 18 | NeatTipView is in charge of presenting the content to the user. 19 | Handles layout calculations and appearance/dissapearance animations. 20 | */ 21 | public class NeatTipView: UIView { 22 | /// Arrow's position. 23 | public var arrowPosition: ArrowPosition 24 | /// Configuration preferences. 25 | public var preferences: NeatViewPreferences 26 | /// The view that will contain this view as a child. 27 | public weak var parentView: UIView? 28 | 29 | var layoutPreferences: NeatLayoutPreferences { 30 | return preferences.layoutPreferences 31 | } 32 | 33 | var animationPreferences: NeatAnimationPreferences { 34 | return preferences.animationPreferences 35 | } 36 | 37 | var overlayPreferences: NeatOverlayStylePreferences { 38 | return preferences.overlayStylePreferences 39 | } 40 | 41 | //MARK: Views initialization 42 | 43 | var bubbleView: NeatBubbleView 44 | 45 | /// Background overlay, customizable using `overlayStylePreferences` 46 | lazy var backgroundView: UIView = { 47 | let view = UIView() 48 | view.translatesAutoresizingMaskIntoConstraints = false 49 | view.backgroundColor = overlayPreferences.backgroundColor 50 | view.alpha = 0 51 | 52 | return view 53 | }() 54 | 55 | //MARK: constraints initialization 56 | /// Bubble's constraints to it's superview. 57 | lazy var bubbleConstraints: [NSLayoutConstraint] = createBubbleConstraints() 58 | 59 | /// Defines Y axis centering using content size and the center point. 60 | var verticalCenterBubbleConstraint: NSLayoutConstraint { 61 | let availableWidth = bubbleView.centerPoint.x - 62 | (layoutPreferences.horizontalInsets + layoutPreferences.contentHorizontalInsets) * 2 - 63 | layoutPreferences.arrowHeight 64 | 65 | let size = bubbleView.attributedString.size( 66 | with: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude) 67 | ) 68 | 69 | let minHeight: CGFloat = 70 70 | 71 | let offset = max(bubbleView.centerPoint.y - max(size.height, minHeight) + layoutPreferences.contentVerticalInsets, 0) 72 | return bubbleView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: offset) 73 | } 74 | 75 | //MARK: APIs 76 | 77 | public init( 78 | superview: UIView, 79 | centerPoint: CGPoint, 80 | attributedString: NSAttributedString, 81 | preferences: NeatViewPreferences = NeatViewPreferences(), 82 | arrowPosition: ArrowPosition = .top 83 | ) { 84 | self.preferences = preferences 85 | self.arrowPosition = arrowPosition == .any ? .top : arrowPosition 86 | self.parentView = superview 87 | 88 | bubbleView = NeatBubbleView( 89 | with: centerPoint, 90 | message: attributedString, 91 | preferences: preferences, 92 | arrowPosition: arrowPosition 93 | ) 94 | 95 | super.init(frame: .zero) 96 | 97 | if let position = whereDoesItFit(in: superview, preferredPosition: arrowPosition) { 98 | self.arrowPosition = position 99 | bubbleView.arrowPosition = position 100 | } 101 | 102 | configureViews() 103 | } 104 | 105 | /// Presents the view inside it's parent, animated. 106 | public func show() { 107 | translatesAutoresizingMaskIntoConstraints = false 108 | 109 | guard let parentView = parentView else { return } 110 | 111 | parentView.addSubview(self) 112 | 113 | var constraints = [ 114 | parentView.topAnchor.constraint(equalTo: topAnchor), 115 | parentView.leadingAnchor.constraint(equalTo: leadingAnchor), 116 | parentView.trailingAnchor.constraint(equalTo: trailingAnchor), 117 | parentView.bottomAnchor.constraint(equalTo: bottomAnchor), 118 | ] 119 | 120 | constraints.append(contentsOf: bubblePositionConstraints) 121 | 122 | NSLayoutConstraint.activate(constraints) 123 | bubbleView.setNeedsDisplay() 124 | bubbleView.layoutIfNeeded() 125 | setNeedsLayout() 126 | layoutIfNeeded() 127 | 128 | UIView.animate( 129 | withDuration: animationPreferences.animationDuration, 130 | delay: 0, 131 | options: animationPreferences.animationCurve, 132 | animations: { [weak self] in 133 | self?.animateAppearance() 134 | self?.layoutIfNeeded() 135 | } 136 | ) 137 | } 138 | 139 | /** 140 | Convenience helper used to create and present a tip in one single call. 141 | Returns a reference to the created tip. 142 | */ 143 | @discardableResult 144 | public static func attach( 145 | to sibling: UIView, 146 | in superview: UIView, 147 | with attributedString: NSAttributedString, 148 | preferences: NeatViewPreferences = NeatViewPreferences(), 149 | arrowPosition: ArrowPosition = .top 150 | ) -> NeatTipView { 151 | let tipView = NeatTipView( 152 | superview: superview, 153 | centerPoint: sibling.center, 154 | attributedString: 155 | attributedString, 156 | preferences: preferences, 157 | arrowPosition: arrowPosition 158 | ) 159 | 160 | switch arrowPosition { 161 | case .top: 162 | tipView.bubbleView.centerPoint = CGPoint(x: sibling.frame.midX, y: sibling.frame.maxY) 163 | case .bottom: 164 | tipView.bubbleView.centerPoint = CGPoint(x: sibling.frame.midX, y: sibling.frame.minY) 165 | case .left: 166 | tipView.bubbleView.centerPoint = CGPoint(x: sibling.frame.maxX, y: sibling.frame.midY) 167 | case .right: 168 | tipView.bubbleView.centerPoint = CGPoint(x: sibling.frame.minX, y: sibling.frame.midY) 169 | case .any: 170 | break 171 | } 172 | 173 | tipView.show() 174 | 175 | return tipView 176 | } 177 | 178 | //MARK: View lifecycle 179 | 180 | required init?(coder aDecoder: NSCoder) { 181 | fatalError("init(coder:) has not been implemented") 182 | } 183 | 184 | public override func didMoveToSuperview() { 185 | super.didMoveToSuperview() 186 | 187 | UIView.animate(withDuration: 0.3, animations: { [weak self] in 188 | self?.backgroundView.alpha = 1 189 | }) 190 | } 191 | 192 | //MARK: Views configuration 193 | /// View setup 194 | private func configureViews() { 195 | addSubviews() 196 | activateConstraints() 197 | addDismissGesture() 198 | } 199 | 200 | private func addDismissGesture() { 201 | let tapGesture = UITapGestureRecognizer( 202 | target: self, 203 | action: #selector(dismiss) 204 | ) 205 | addGestureRecognizer(tapGesture) 206 | } 207 | 208 | /// Dismiss the tip and removes it from it's parent view hierarchy. 209 | @objc 210 | func dismiss() { 211 | UIView.animate( 212 | withDuration: animationPreferences.animationDuration, 213 | delay: 0, 214 | options: animationPreferences.animationCurve, 215 | animations: { [weak self] in 216 | self?.animateDissapearance() 217 | }, 218 | completion: { [weak self] _ in 219 | self?.removeFromSuperview() 220 | } 221 | ) 222 | } 223 | 224 | private func addSubviews() { 225 | backgroundColor = .clear 226 | addSubview(backgroundView) 227 | addSubview(bubbleView) 228 | bringSubviewToFront(bubbleView) 229 | } 230 | 231 | private func activateConstraints() { 232 | var constraints = [ 233 | backgroundView.leadingAnchor.constraint(equalTo: leadingAnchor), 234 | backgroundView.trailingAnchor.constraint(equalTo: trailingAnchor), 235 | backgroundView.topAnchor.constraint(equalTo: topAnchor), 236 | backgroundView.bottomAnchor.constraint(equalTo: bottomAnchor), 237 | ] 238 | 239 | constraints.append(contentsOf: bubbleConstraints) 240 | 241 | NSLayoutConstraint.activate(constraints) 242 | } 243 | 244 | private func animateAppearance() { 245 | switch animationPreferences.appearanceAnimationType { 246 | case .fromLeft: 247 | bubbleView.frame.origin.x += animationPreferences.animationOffset 248 | case .fromRight: 249 | bubbleView.frame.origin.x -= animationPreferences.animationOffset 250 | case .fromTop: 251 | bubbleView.frame.origin.y += animationPreferences.animationOffset 252 | case .fromBottom: 253 | bubbleView.frame.origin.y -= animationPreferences.animationOffset 254 | case .fadeIn: 255 | break 256 | } 257 | } 258 | 259 | private func animateDissapearance() { 260 | alpha = 0 261 | switch animationPreferences.disappearanceAnimationType { 262 | case .toLeft: 263 | bubbleView.frame.origin.x -= animationPreferences.animationOffset 264 | case .toRight: 265 | bubbleView.frame.origin.x += animationPreferences.animationOffset 266 | case .toTop: 267 | bubbleView.frame.origin.y -= animationPreferences.animationOffset 268 | case .toBottom: 269 | bubbleView.frame.origin.y += animationPreferences.animationOffset 270 | case .fadeOut: 271 | break 272 | } 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /NeatTipView/Classes/View/NeatTipViewConstraintsInitialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeatTipViewConstraintsInitialization.swift 3 | // NeatTipView 4 | // 5 | // Created by Germán Stábile on 7/22/19. 6 | // 7 | 8 | import UIKit 9 | 10 | extension NeatTipView { 11 | 12 | //MARK: Bubble constraints 13 | 14 | func createBubbleConstraints() -> [NSLayoutConstraint] { 15 | return arrowPosition.isVerticalArrow ? verticalBubbleConstraints : horizontalBubbleConstraints 16 | } 17 | 18 | var verticalBubbleConstraints: [NSLayoutConstraint] { 19 | return [ 20 | bubbleView.leadingAnchor.constraint( 21 | greaterThanOrEqualTo: leadingAnchor, 22 | constant: layoutPreferences.horizontalInsets 23 | ), 24 | bubbleView.trailingAnchor.constraint( 25 | lessThanOrEqualTo: trailingAnchor, 26 | constant: -layoutPreferences.horizontalInsets 27 | ), 28 | ] 29 | } 30 | 31 | var horizontalBubbleConstraints: [NSLayoutConstraint] { 32 | return [ 33 | bubbleView.bottomAnchor.constraint( 34 | lessThanOrEqualTo: safeAreaLayoutGuide.bottomAnchor, 35 | constant: -layoutPreferences.verticalInsets 36 | ), 37 | bubbleToViewHorizontalConstraint 38 | ] 39 | } 40 | 41 | var bubbleToViewHorizontalConstraint: NSLayoutConstraint { 42 | if arrowPosition == .left { 43 | return bubbleView.trailingAnchor.constraint( 44 | lessThanOrEqualTo: trailingAnchor, 45 | constant: -layoutPreferences.horizontalInsets 46 | ) 47 | } else { 48 | return bubbleView.leadingAnchor.constraint( 49 | greaterThanOrEqualTo: leadingAnchor, 50 | constant: layoutPreferences.horizontalInsets 51 | ) 52 | } 53 | } 54 | 55 | var bubblePositionConstraints: [NSLayoutConstraint] { 56 | let size = bubbleView.attributedString.size() 57 | let bubbleLeadingConstraint = bubbleView.leadingAnchor.constraint( 58 | equalTo: leadingAnchor, 59 | constant: bubbleView.centerPoint.x + layoutPreferences.contentHorizontalInsets - size.width / 2 60 | ) 61 | bubbleLeadingConstraint.priority = .defaultLow 62 | 63 | switch arrowPosition { 64 | case .any, .top: 65 | return [ 66 | bubbleView.topAnchor.constraint( 67 | equalTo: topAnchor, 68 | constant: bubbleView.centerPoint.y + layoutPreferences.contentVerticalInsets 69 | ), 70 | bubbleLeadingConstraint, 71 | bubbleView.bottomAnchor.constraint(lessThanOrEqualTo: safeAreaLayoutGuide.bottomAnchor), 72 | ] 73 | case .bottom: 74 | return [ 75 | bubbleView.bottomAnchor.constraint( 76 | equalTo: topAnchor, 77 | constant: bubbleView.centerPoint.y - layoutPreferences.contentVerticalInsets 78 | ), 79 | bubbleLeadingConstraint 80 | ] 81 | case .left: 82 | return [ 83 | bubbleView.leadingAnchor.constraint( 84 | equalTo: leadingAnchor, 85 | constant: bubbleView.centerPoint.x + layoutPreferences.contentHorizontalInsets 86 | ), 87 | verticalCenterBubbleConstraint 88 | ] 89 | case .right: 90 | return [ 91 | bubbleView.trailingAnchor.constraint( 92 | equalTo: leadingAnchor, 93 | constant: bubbleView.centerPoint.x - layoutPreferences.contentHorizontalInsets 94 | ), 95 | verticalCenterBubbleConstraint 96 | ] 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /NeatTipView/Classes/View/NeatTipViewTipHelpers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NeatTipViewTipHelpers.swift 3 | // NeatTipView 4 | // 5 | // Created by Mauricio Cousillas on 8/23/19. 6 | // 7 | 8 | import Foundation 9 | 10 | extension NeatTipView { 11 | 12 | func tipFits(in superview: UIView, for arrowPosition: ArrowPosition) -> Bool { 13 | let availableWidth = superview.bounds.width - 14 | (layoutPreferences.horizontalInsets + layoutPreferences.contentHorizontalInsets) * 2 15 | let availableHeight = superview.bounds.height - 16 | superview.safeAreaInsets.bottom - superview.safeAreaInsets.top - 17 | (layoutPreferences.verticalInsets + layoutPreferences.contentVerticalInsets) * 2 18 | 19 | switch arrowPosition { 20 | case .bottom: 21 | return fitsAtTop(in: superview, with: availableWidth) 22 | case .top: 23 | return fitsAtBottom(in: superview, with: availableWidth) 24 | case .left: 25 | return fitsInRight(in: superview, with: availableHeight) 26 | case .right: 27 | return fitsInLeft(in: superview, with: availableHeight) 28 | default: 29 | return false 30 | } 31 | } 32 | 33 | func fitsAtBottom(in superview: UIView, with availableWidth: CGFloat) -> Bool { 34 | let availableHeight = superview.bounds.height - superview.safeAreaInsets.bottom - bubbleView.centerPoint.y - 35 | (layoutPreferences.verticalInsets + layoutPreferences.contentVerticalInsets) * 2 - 36 | layoutPreferences.arrowHeight 37 | 38 | let size = bubbleView.attributedString.size(with: CGSize(width: availableWidth, 39 | height: CGFloat.greatestFiniteMagnitude)) 40 | 41 | return availableHeight > size.height 42 | } 43 | 44 | func fitsAtTop(in superview: UIView, with availableWidth: CGFloat) -> Bool { 45 | let availableHeight = bubbleView.centerPoint.y - superview.safeAreaInsets.top - 46 | (layoutPreferences.verticalInsets + layoutPreferences.contentVerticalInsets) * 2 - 47 | layoutPreferences.arrowHeight 48 | 49 | let size = bubbleView.attributedString.size(with: CGSize(width: availableWidth, 50 | height: CGFloat.greatestFiniteMagnitude)) 51 | 52 | return availableHeight > size.height 53 | } 54 | 55 | func fitsInRight(in superview: UIView, with availableHeight: CGFloat) -> Bool { 56 | let availableWidth = superview.bounds.width - bubbleView.centerPoint.x - 57 | (layoutPreferences.horizontalInsets + layoutPreferences.contentHorizontalInsets) * 2 - 58 | layoutPreferences.arrowHeight 59 | 60 | guard availableWidth > layoutPreferences.minWidth else { return false } 61 | 62 | let size = bubbleView.attributedString.size(with: CGSize(width: availableWidth, 63 | height: CGFloat.greatestFiniteMagnitude)) 64 | 65 | return availableHeight > size.height 66 | } 67 | 68 | func fitsInLeft(in superview: UIView, with availableHeight: CGFloat) -> Bool { 69 | let availableWidth = bubbleView.centerPoint.x - 70 | (layoutPreferences.horizontalInsets + layoutPreferences.contentHorizontalInsets) * 2 - 71 | layoutPreferences.arrowHeight 72 | 73 | guard availableWidth > layoutPreferences.minWidth else { return false } 74 | 75 | let size = bubbleView.attributedString.size(with: CGSize(width: availableWidth, 76 | height: CGFloat.greatestFiniteMagnitude)) 77 | 78 | return availableHeight > size.height 79 | } 80 | 81 | func whereDoesItFit(in superview: UIView, 82 | preferredPosition: ArrowPosition) -> ArrowPosition? { 83 | if tipFits(in: superview, for: preferredPosition) { 84 | return preferredPosition 85 | } else if tipFits(in: superview, for: preferredPosition.counterpart) { 86 | return preferredPosition.counterpart 87 | } 88 | 89 | let remainingPositions = preferredPosition.isVerticalArrow ? 90 | ArrowPosition.horizontalPositions : 91 | ArrowPosition.verticalPositions 92 | 93 | return remainingPositions.first { tipFits(in: superview, for: $0) } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /NeatTipViewLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/NeatTipViewLogo.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2 | [![Version](https://img.shields.io/cocoapods/v/NeatTipView.svg?style=flat&colorA=000000)](http://cocoapods.org/pods/NeatTipView) 3 | [![License](https://img.shields.io/cocoapods/l/NeatTipView.svg?style=flat&colorA=000000)](http://cocoapods.org/pods/NeatTipView) 4 | [![Platform](https://img.shields.io/cocoapods/p/NeatTipView.svg?style=flat&colorA=000000)](http://cocoapods.org/pods/NeatTipView) 5 | 6 | 7 | ## [Live Docs](https://rootstrap.github.io/NeatTipView/) 8 | 9 | ## What is it? 10 | NeatTipView allows you to display message tooltips that can be used as call to actions or informative tips. 11 | - [x] Allows Different tip positionings. 12 | - [x] Multiple animation styles. 13 | - [x] Smart placement for dynamic strings. 14 | - [x] Full NSAttributtedString support. 15 | 16 | 17 | 18 | ## Installation 19 | 20 | NeatTipView is available through [CocoaPods](http://cocoapods.org) and [Carthage](). 21 | ### Cocoapods 22 | To install it, simply add the following line to your Podfile: 23 | ```ruby 24 | pod "NeatTipView" 25 | ``` 26 | 27 | ### Carthage 28 | 29 | To install it, simply add the following line to your Cartfile: 30 | ``` 31 | github "rootstrap/NeatTipView" 32 | ``` 33 | 34 | ## Usage 35 | 36 | ### 1. Customize your preferences 37 | Preferences are encapsulated inside the `NeatViewPreferences` struct, check the inline docs for more info about which customization points are available. 38 | 39 | Example: 40 | 41 | ```swift 42 | var preferences = NeatViewPreferences() 43 | preferences.animationPreferences.appearanceAnimationType = .fromBottom 44 | preferences.animationPreferences.disappearanceAnimationType = .toBottom 45 | ``` 46 | 47 | ### 2. Initialize and Present the tip 48 | ```swift 49 | let tipView = NeatTipView( 50 | superview: view, 51 | centerPoint: center, 52 | attributedString: attributedString(), 53 | preferences: preferences, 54 | arrowPosition: arrowPosition 55 | ) 56 | tipView.show() 57 | ``` 58 | 59 | ## Example 60 | 61 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 62 | 63 | 64 | ## License 65 | 66 | NeatTipView is available under the MIT license. See the LICENSE file for more info. 67 | 68 | ## Credits 69 | 70 | **NeatTipView** is maintained by [Rootstrap](http://www.rootstrap.com) with the help of our [contributors](https://github.com/rootstrap/NeatTipView/contributors). 71 | 72 | [](http://www.rootstrap.com) 73 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /docs/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

NeatTipView 1.0.0 Docs (30% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 78 |
79 |
80 |
81 |

Classes

82 |

The following classes are available globally.

83 | 84 |
85 |
86 |
87 |
    88 |
  • 89 |
    90 | 91 | 92 | 93 | NeatBubbleView 94 | 95 |
    96 |
    97 |
    98 |
    99 |
    100 |
    101 |

    NeatBubbleView renders the actual tooltip using the defined preferences and string. 102 | You can use this view without depending on NeatTipView if you need something custom, 103 | or you don’t want a full-screen overlay.

    104 | 105 | See more 106 |
    107 |
    108 |

    Declaration

    109 |
    110 |

    Swift

    111 |
    public class NeatBubbleView : UIView
    112 | 113 |
    114 |
    115 |
    116 | Show on GitHub 117 |
    118 |
    119 |
    120 |
  • 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | NeatTipView 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    NeatTipView is in charge of presenting the content to the user. 139 | Handles layout calculations and appearance/dissapearance animations.

    140 | 141 | See more 142 |
    143 |
    144 |

    Declaration

    145 |
    146 |

    Swift

    147 |
    public class NeatTipView : UIView
    148 | 149 |
    150 |
    151 |
    152 | Show on GitHub 153 |
    154 |
    155 |
    156 |
  • 157 |
158 |
159 |
160 |
161 | 165 |
166 |
167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /docs/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

NeatTipView 1.0.0 Docs (30% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 78 |
79 |
80 |
81 |

Enumerations

82 |

The following enumerations are available globally.

83 | 84 |
85 |
86 |
87 |
    88 |
  • 89 |
    90 | 91 | 92 | 93 | AppearanceAnimationType 94 | 95 |
    96 |
    97 |
    98 |
    99 |
    100 |
    101 |

    AppearanceAnimationType defines all available appearance animations, 102 | this will define how the View is presented each time .show() is called

    103 | 104 | See more 105 |
    106 |
    107 |

    Declaration

    108 |
    109 |

    Swift

    110 |
    public enum AppearanceAnimationType
    111 | 112 |
    113 |
    114 |
    115 | Show on GitHub 116 |
    117 |
    118 |
    119 |
  • 120 |
  • 121 |
    122 | 123 | 124 | 125 | DisappearanceAnimationType 126 | 127 |
    128 |
    129 |
    130 |
    131 |
    132 |
    133 |

    AppearanceAnimationType defines all available dismiss animations, 134 | this will define how the View is presented each time .dismiss() is called

    135 | 136 | See more 137 |
    138 |
    139 |

    Declaration

    140 |
    141 |

    Swift

    142 |
    public enum DisappearanceAnimationType
    143 | 144 |
    145 |
    146 |
    147 | Show on GitHub 148 |
    149 |
    150 |
    151 |
  • 152 |
153 |
154 |
155 |
    156 |
  • 157 |
    158 | 159 | 160 | 161 | ArrowPosition 162 | 163 |
    164 |
    165 |
    166 |
    167 |
    168 |
    169 |

    ArrowPosition defines the available orientations of the view’s tip. 170 | Each case defines where the tip is pointing to (so top will point upwards for example)

    171 | 172 | See more 173 |
    174 |
    175 |

    Declaration

    176 |
    177 |

    Swift

    178 |
    public enum ArrowPosition
    179 | 180 |
    181 |
    182 |
    183 | Show on GitHub 184 |
    185 |
    186 |
    187 |
  • 188 |
189 |
190 |
191 |
192 | 196 |
197 |
198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /docs/Enums/AppearanceAnimationType.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AppearanceAnimationType Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

NeatTipView 1.0.0 Docs (30% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 78 |
79 |
80 |
81 |

AppearanceAnimationType

82 |
83 |
84 |
public enum AppearanceAnimationType
85 | 86 |
87 |
88 |

AppearanceAnimationType defines all available appearance animations, 89 | this will define how the View is presented each time .show() is called

90 | 91 |
92 |
93 |
94 |
    95 |
  • 96 |
    97 | 98 | 99 | 100 | fromTop 101 | 102 |
    103 |
    104 |
    105 |
    106 |
    107 |
    108 |

    Undocumented

    109 | 110 |
    111 |
    112 |

    Declaration

    113 |
    114 |

    Swift

    115 |
    case fromTop
    116 | 117 |
    118 |
    119 |
    120 | Show on GitHub 121 |
    122 |
    123 |
    124 |
  • 125 |
  • 126 |
    127 | 128 | 129 | 130 | fromBottom 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    Undocumented

    139 | 140 |
    141 |
    142 |

    Declaration

    143 |
    144 |

    Swift

    145 |
    case fromBottom
    146 | 147 |
    148 |
    149 |
    150 | Show on GitHub 151 |
    152 |
    153 |
    154 |
  • 155 |
  • 156 |
    157 | 158 | 159 | 160 | fromLeft 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    Undocumented

    169 | 170 |
    171 |
    172 |

    Declaration

    173 |
    174 |

    Swift

    175 |
    case fromLeft
    176 | 177 |
    178 |
    179 |
    180 | Show on GitHub 181 |
    182 |
    183 |
    184 |
  • 185 |
  • 186 |
    187 | 188 | 189 | 190 | fromRight 191 | 192 |
    193 |
    194 |
    195 |
    196 |
    197 |
    198 |

    Undocumented

    199 | 200 |
    201 |
    202 |

    Declaration

    203 |
    204 |

    Swift

    205 |
    case fromRight
    206 | 207 |
    208 |
    209 |
    210 | Show on GitHub 211 |
    212 |
    213 |
    214 |
  • 215 |
  • 216 |
    217 | 218 | 219 | 220 | fadeIn 221 | 222 |
    223 |
    224 |
    225 |
    226 |
    227 |
    228 |

    Undocumented

    229 | 230 |
    231 |
    232 |

    Declaration

    233 |
    234 |

    Swift

    235 |
    case fadeIn
    236 | 237 |
    238 |
    239 |
    240 | Show on GitHub 241 |
    242 |
    243 |
    244 |
  • 245 |
246 |
247 |
248 |
249 | 253 |
254 |
255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /docs/Enums/ArrowPosition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArrowPosition Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

NeatTipView 1.0.0 Docs (30% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 78 |
79 |
80 |
81 |

ArrowPosition

82 |
83 |
84 |
public enum ArrowPosition
85 | 86 |
87 |
88 |

ArrowPosition defines the available orientations of the view’s tip. 89 | Each case defines where the tip is pointing to (so top will point upwards for example)

90 | 91 |
92 |
93 |
94 |
    95 |
  • 96 |
    97 | 98 | 99 | 100 | top 101 | 102 |
    103 |
    104 |
    105 |
    106 |
    107 |
    108 |

    Undocumented

    109 | 110 |
    111 |
    112 |

    Declaration

    113 |
    114 |

    Swift

    115 |
    case top
    116 | 117 |
    118 |
    119 |
    120 | Show on GitHub 121 |
    122 |
    123 |
    124 |
  • 125 |
  • 126 |
    127 | 128 | 129 | 130 | bottom 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    Undocumented

    139 | 140 |
    141 |
    142 |

    Declaration

    143 |
    144 |

    Swift

    145 |
    case bottom
    146 | 147 |
    148 |
    149 |
    150 | Show on GitHub 151 |
    152 |
    153 |
    154 |
  • 155 |
  • 156 |
    157 | 158 | 159 | 160 | left 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    Undocumented

    169 | 170 |
    171 |
    172 |

    Declaration

    173 |
    174 |

    Swift

    175 |
    case left
    176 | 177 |
    178 |
    179 |
    180 | Show on GitHub 181 |
    182 |
    183 |
    184 |
  • 185 |
  • 186 |
    187 | 188 | 189 | 190 | right 191 | 192 |
    193 |
    194 |
    195 |
    196 |
    197 |
    198 |

    Undocumented

    199 | 200 |
    201 |
    202 |

    Declaration

    203 |
    204 |

    Swift

    205 |
    case right
    206 | 207 |
    208 |
    209 |
    210 | Show on GitHub 211 |
    212 |
    213 |
    214 |
  • 215 |
  • 216 |
    217 | 218 | 219 | 220 | any 221 | 222 |
    223 |
    224 |
    225 |
    226 |
    227 |
    228 |

    Undocumented

    229 | 230 |
    231 |
    232 |

    Declaration

    233 |
    234 |

    Swift

    235 |
    case any
    236 | 237 |
    238 |
    239 |
    240 | Show on GitHub 241 |
    242 |
    243 |
    244 |
  • 245 |
246 |
247 |
248 |
249 | 253 |
254 |
255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /docs/Structs/NeatOverlayStylePreferences.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NeatOverlayStylePreferences Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

NeatTipView 1.0.0 Docs (30% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 78 |
79 |
80 |
81 |

NeatOverlayStylePreferences

82 |
83 |
84 |
public struct NeatOverlayStylePreferences
85 | 86 |
87 |
88 |

Overlay style properties

89 | 90 |
91 |
92 |
93 |
    94 |
  • 95 |
    96 | 97 | 98 | 99 | backgroundColor 100 | 101 |
    102 |
    103 |
    104 |
    105 |
    106 |
    107 |

    Undocumented

    108 | 109 |
    110 |
    111 |

    Declaration

    112 |
    113 |

    Swift

    114 |
    public var backgroundColor: UIColor
    115 | 116 |
    117 |
    118 |
    119 | Show on GitHub 120 |
    121 |
    122 |
    123 |
  • 124 |
125 |
126 |
127 |
128 | 132 |
133 |
134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 0% 23 | 24 | 25 | 0% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token, .item .direct-link { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; 223 | transition: all 300ms; } 224 | .item .token-open { 225 | margin-left: 0px; } 226 | .item .discouraged { 227 | text-decoration: line-through; } 228 | .item .declaration-note { 229 | font-size: .85em; 230 | color: gray; 231 | font-style: italic; } 232 | 233 | .pointer-container { 234 | border-bottom: 1px solid #e2e2e2; 235 | left: -23px; 236 | padding-bottom: 13px; 237 | position: relative; 238 | width: 110%; } 239 | 240 | .pointer { 241 | background: #f9f9f9; 242 | border-left: 1px solid #e2e2e2; 243 | border-top: 1px solid #e2e2e2; 244 | height: 12px; 245 | left: 21px; 246 | top: -7px; 247 | -webkit-transform: rotate(45deg); 248 | -moz-transform: rotate(45deg); 249 | -o-transform: rotate(45deg); 250 | transform: rotate(45deg); 251 | position: absolute; 252 | width: 12px; } 253 | 254 | .height-container { 255 | display: none; 256 | left: -25px; 257 | padding: 0 25px; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #f9f9f9; 263 | border-bottom: 1px solid #e2e2e2; 264 | left: -25px; 265 | position: relative; 266 | width: 100%; 267 | padding-top: 10px; 268 | padding-bottom: 5px; } 269 | 270 | .aside, .language { 271 | padding: 6px 12px; 272 | margin: 12px 0; 273 | border-left: 5px solid #dddddd; 274 | overflow-y: hidden; } 275 | .aside .aside-title, .language .aside-title { 276 | font-size: 9px; 277 | letter-spacing: 2px; 278 | text-transform: uppercase; 279 | padding-bottom: 0; 280 | margin: 0; 281 | color: #aaa; 282 | -webkit-user-select: none; } 283 | .aside p:last-child, .language p:last-child { 284 | margin-bottom: 0; } 285 | 286 | .language { 287 | border-left: 5px solid #cde9f4; } 288 | .language .aside-title { 289 | color: #4b8afb; } 290 | 291 | .aside-warning, .aside-deprecated, .aside-unavailable { 292 | border-left: 5px solid #ff6666; } 293 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 294 | color: #ff0000; } 295 | 296 | .graybox { 297 | border-collapse: collapse; 298 | width: 100%; } 299 | .graybox p { 300 | margin: 0; 301 | word-break: break-word; 302 | min-width: 50px; } 303 | .graybox td { 304 | border: 1px solid #e2e2e2; 305 | padding: 5px 25px 5px 10px; 306 | vertical-align: middle; } 307 | .graybox tr td:first-of-type { 308 | text-align: right; 309 | padding: 7px; 310 | vertical-align: top; 311 | word-break: normal; 312 | width: 40px; } 313 | 314 | .slightly-smaller { 315 | font-size: 0.9em; } 316 | 317 | #footer { 318 | position: relative; 319 | top: 10px; 320 | bottom: 0px; 321 | margin-left: 25px; } 322 | #footer p { 323 | margin: 0; 324 | color: #aaa; 325 | font-size: 0.8em; } 326 | 327 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 328 | display: none; } 329 | html.dash .main-content { 330 | width: 980px; 331 | margin-left: 0; 332 | border: none; 333 | width: 100%; 334 | top: 0; 335 | padding-bottom: 0; } 336 | html.dash .height-container { 337 | display: block; } 338 | html.dash .item .token { 339 | margin-left: 0; } 340 | html.dash .content-wrapper { 341 | width: auto; } 342 | html.dash #footer { 343 | position: static; } 344 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.neattipview 7 | CFBundleName 8 | NeatTipView 9 | DocSetPlatformFamily 10 | neattipview 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/Classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

NeatTipView 1.0.0 Docs (30% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 78 |
79 |
80 |
81 |

Classes

82 |

The following classes are available globally.

83 | 84 |
85 |
86 |
87 |
    88 |
  • 89 |
    90 | 91 | 92 | 93 | NeatBubbleView 94 | 95 |
    96 |
    97 |
    98 |
    99 |
    100 |
    101 |

    NeatBubbleView renders the actual tooltip using the defined preferences and string. 102 | You can use this view without depending on NeatTipView if you need something custom, 103 | or you don’t want a full-screen overlay.

    104 | 105 | See more 106 |
    107 |
    108 |

    Declaration

    109 |
    110 |

    Swift

    111 |
    public class NeatBubbleView : UIView
    112 | 113 |
    114 |
    115 |
    116 | Show on GitHub 117 |
    118 |
    119 |
    120 |
  • 121 |
122 |
123 |
124 |
    125 |
  • 126 |
    127 | 128 | 129 | 130 | NeatTipView 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    NeatTipView is in charge of presenting the content to the user. 139 | Handles layout calculations and appearance/dissapearance animations.

    140 | 141 | See more 142 |
    143 |
    144 |

    Declaration

    145 |
    146 |

    Swift

    147 |
    public class NeatTipView : UIView
    148 | 149 |
    150 |
    151 |
    152 | Show on GitHub 153 |
    154 |
    155 |
    156 |
  • 157 |
158 |
159 |
160 |
161 | 165 |
166 |
167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/Enums.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Enumerations Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

NeatTipView 1.0.0 Docs (30% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 78 |
79 |
80 |
81 |

Enumerations

82 |

The following enumerations are available globally.

83 | 84 |
85 |
86 |
87 |
    88 |
  • 89 |
    90 | 91 | 92 | 93 | AppearanceAnimationType 94 | 95 |
    96 |
    97 |
    98 |
    99 |
    100 |
    101 |

    AppearanceAnimationType defines all available appearance animations, 102 | this will define how the View is presented each time .show() is called

    103 | 104 | See more 105 |
    106 |
    107 |

    Declaration

    108 |
    109 |

    Swift

    110 |
    public enum AppearanceAnimationType
    111 | 112 |
    113 |
    114 |
    115 | Show on GitHub 116 |
    117 |
    118 |
    119 |
  • 120 |
  • 121 |
    122 | 123 | 124 | 125 | DisappearanceAnimationType 126 | 127 |
    128 |
    129 |
    130 |
    131 |
    132 |
    133 |

    AppearanceAnimationType defines all available dismiss animations, 134 | this will define how the View is presented each time .dismiss() is called

    135 | 136 | See more 137 |
    138 |
    139 |

    Declaration

    140 |
    141 |

    Swift

    142 |
    public enum DisappearanceAnimationType
    143 | 144 |
    145 |
    146 |
    147 | Show on GitHub 148 |
    149 |
    150 |
    151 |
  • 152 |
153 |
154 |
155 |
    156 |
  • 157 |
    158 | 159 | 160 | 161 | ArrowPosition 162 | 163 |
    164 |
    165 |
    166 |
    167 |
    168 |
    169 |

    ArrowPosition defines the available orientations of the view’s tip. 170 | Each case defines where the tip is pointing to (so top will point upwards for example)

    171 | 172 | See more 173 |
    174 |
    175 |

    Declaration

    176 |
    177 |

    Swift

    178 |
    public enum ArrowPosition
    179 | 180 |
    181 |
    182 |
    183 | Show on GitHub 184 |
    185 |
    186 |
    187 |
  • 188 |
189 |
190 |
191 |
192 | 196 |
197 |
198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/Enums/ArrowPosition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArrowPosition Enumeration Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

NeatTipView 1.0.0 Docs (30% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 78 |
79 |
80 |
81 |

ArrowPosition

82 |
83 |
84 |
public enum ArrowPosition
85 | 86 |
87 |
88 |

ArrowPosition defines the available orientations of the view’s tip. 89 | Each case defines where the tip is pointing to (so top will point upwards for example)

90 | 91 |
92 |
93 |
94 |
    95 |
  • 96 |
    97 | 98 | 99 | 100 | top 101 | 102 |
    103 |
    104 |
    105 |
    106 |
    107 |
    108 |

    Undocumented

    109 | 110 |
    111 |
    112 |

    Declaration

    113 |
    114 |

    Swift

    115 |
    case top
    116 | 117 |
    118 |
    119 |
    120 | Show on GitHub 121 |
    122 |
    123 |
    124 |
  • 125 |
  • 126 |
    127 | 128 | 129 | 130 | bottom 131 | 132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 |

    Undocumented

    139 | 140 |
    141 |
    142 |

    Declaration

    143 |
    144 |

    Swift

    145 |
    case bottom
    146 | 147 |
    148 |
    149 |
    150 | Show on GitHub 151 |
    152 |
    153 |
    154 |
  • 155 |
  • 156 |
    157 | 158 | 159 | 160 | left 161 | 162 |
    163 |
    164 |
    165 |
    166 |
    167 |
    168 |

    Undocumented

    169 | 170 |
    171 |
    172 |

    Declaration

    173 |
    174 |

    Swift

    175 |
    case left
    176 | 177 |
    178 |
    179 |
    180 | Show on GitHub 181 |
    182 |
    183 |
    184 |
  • 185 |
  • 186 |
    187 | 188 | 189 | 190 | right 191 | 192 |
    193 |
    194 |
    195 |
    196 |
    197 |
    198 |

    Undocumented

    199 | 200 |
    201 |
    202 |

    Declaration

    203 |
    204 |

    Swift

    205 |
    case right
    206 | 207 |
    208 |
    209 |
    210 | Show on GitHub 211 |
    212 |
    213 |
    214 |
  • 215 |
  • 216 |
    217 | 218 | 219 | 220 | any 221 | 222 |
    223 |
    224 |
    225 |
    226 |
    227 |
    228 |

    Undocumented

    229 | 230 |
    231 |
    232 |

    Declaration

    233 |
    234 |

    Swift

    235 |
    case any
    236 | 237 |
    238 |
    239 |
    240 | Show on GitHub 241 |
    242 |
    243 |
    244 |
  • 245 |
246 |
247 |
248 |
249 | 253 |
254 |
255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/Structs/NeatOverlayStylePreferences.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NeatOverlayStylePreferences Structure Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

NeatTipView 1.0.0 Docs (30% documented)

18 |

View on GitHub

19 |
20 |
21 |
22 | 27 |
28 |
29 | 78 |
79 |
80 |
81 |

NeatOverlayStylePreferences

82 |
83 |
84 |
public struct NeatOverlayStylePreferences
85 | 86 |
87 |
88 |

Overlay style properties

89 | 90 |
91 |
92 |
93 |
    94 |
  • 95 |
    96 | 97 | 98 | 99 | backgroundColor 100 | 101 |
    102 |
    103 |
    104 |
    105 |
    106 |
    107 |

    Undocumented

    108 | 109 |
    110 |
    111 |

    Declaration

    112 |
    113 |

    Swift

    114 |
    public var backgroundColor: UIColor
    115 | 116 |
    117 |
    118 |
    119 | Show on GitHub 120 |
    121 |
    122 |
    123 |
  • 124 |
125 |
126 |
127 |
128 | 132 |
133 |
134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 30% 23 | 24 | 25 | 30% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token, .item .direct-link { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; 223 | transition: all 300ms; } 224 | .item .token-open { 225 | margin-left: 0px; } 226 | .item .discouraged { 227 | text-decoration: line-through; } 228 | .item .declaration-note { 229 | font-size: .85em; 230 | color: gray; 231 | font-style: italic; } 232 | 233 | .pointer-container { 234 | border-bottom: 1px solid #e2e2e2; 235 | left: -23px; 236 | padding-bottom: 13px; 237 | position: relative; 238 | width: 110%; } 239 | 240 | .pointer { 241 | background: #f9f9f9; 242 | border-left: 1px solid #e2e2e2; 243 | border-top: 1px solid #e2e2e2; 244 | height: 12px; 245 | left: 21px; 246 | top: -7px; 247 | -webkit-transform: rotate(45deg); 248 | -moz-transform: rotate(45deg); 249 | -o-transform: rotate(45deg); 250 | transform: rotate(45deg); 251 | position: absolute; 252 | width: 12px; } 253 | 254 | .height-container { 255 | display: none; 256 | left: -25px; 257 | padding: 0 25px; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #f9f9f9; 263 | border-bottom: 1px solid #e2e2e2; 264 | left: -25px; 265 | position: relative; 266 | width: 100%; 267 | padding-top: 10px; 268 | padding-bottom: 5px; } 269 | 270 | .aside, .language { 271 | padding: 6px 12px; 272 | margin: 12px 0; 273 | border-left: 5px solid #dddddd; 274 | overflow-y: hidden; } 275 | .aside .aside-title, .language .aside-title { 276 | font-size: 9px; 277 | letter-spacing: 2px; 278 | text-transform: uppercase; 279 | padding-bottom: 0; 280 | margin: 0; 281 | color: #aaa; 282 | -webkit-user-select: none; } 283 | .aside p:last-child, .language p:last-child { 284 | margin-bottom: 0; } 285 | 286 | .language { 287 | border-left: 5px solid #cde9f4; } 288 | .language .aside-title { 289 | color: #4b8afb; } 290 | 291 | .aside-warning, .aside-deprecated, .aside-unavailable { 292 | border-left: 5px solid #ff6666; } 293 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 294 | color: #ff0000; } 295 | 296 | .graybox { 297 | border-collapse: collapse; 298 | width: 100%; } 299 | .graybox p { 300 | margin: 0; 301 | word-break: break-word; 302 | min-width: 50px; } 303 | .graybox td { 304 | border: 1px solid #e2e2e2; 305 | padding: 5px 25px 5px 10px; 306 | vertical-align: middle; } 307 | .graybox tr td:first-of-type { 308 | text-align: right; 309 | padding: 7px; 310 | vertical-align: top; 311 | word-break: normal; 312 | width: 40px; } 313 | 314 | .slightly-smaller { 315 | font-size: 0.9em; } 316 | 317 | #footer { 318 | position: relative; 319 | top: 10px; 320 | bottom: 0px; 321 | margin-left: 25px; } 322 | #footer p { 323 | margin: 0; 324 | color: #aaa; 325 | font-size: 0.8em; } 326 | 327 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 328 | display: none; } 329 | html.dash .main-content { 330 | width: 980px; 331 | margin-left: 0; 332 | border: none; 333 | width: 100%; 334 | top: 0; 335 | padding-bottom: 0; } 336 | html.dash .height-container { 337 | display: block; } 338 | html.dash .item .token { 339 | margin-left: 0; } 340 | html.dash .content-wrapper { 341 | width: auto; } 342 | html.dash #footer { 343 | position: static; } 344 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/docs/docsets/NeatTipView.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/docs/docsets/NeatTipView.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/docs/docsets/NeatTipView.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NeatTipView Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

NeatTipView Docs (0% documented)

17 |
18 |
19 |
20 | 25 |
26 |
27 | 31 |
32 |
33 |
34 | 35 |

NeatTipView

36 | 37 |

Version 38 | License 39 | Platform

40 |

What is it?

41 | 42 |

NeatTipView allows you to display message tooltips that can be used as call to actions or informative tips.

43 | 44 |
    45 |
  • [x] Allows Different tip positionings.
  • 46 |
  • [x] Multiple animation styles.
  • 47 |
  • [x] Smart placement for dynamic strings.
  • 48 |
  • [x] Full NSAttributtedString support.
  • 49 |
50 |

Installation

51 | 52 |

NeatTipView is available through CocoaPods and Carthage.

53 |

Cocoapods

54 | 55 |

To install it, simply add the following line to your Podfile:

56 |
pod "NeatTipView"
 57 | 
58 |

Carthage

59 | 60 |

To install it, simply add the following line to your Cartfile:

61 |
github "rootstrap/NeatTipView"
 62 | 
63 |

Usage

64 |

1. Customize your preferences

65 | 66 |

Preferences are encapsulated inside the NeatViewPreferences struct, check the inline docs for more info about which customization points are available.

67 | 68 |

Example:

69 |
  var preferences = NeatViewPreferences()
 70 |   preferences.animationPreferences.appearanceAnimationType = .fromBottom
 71 |   preferences.animationPreferences.disappearanceAnimationType = .toBottom
 72 | 
73 |

2. Initialize and Present the tip

74 |
  let tipView = NeatTipView(
 75 |     superview: view,
 76 |     centerPoint: center,
 77 |     attributedString: attributedString(),
 78 |                             preferences: preferences,
 79 |                             arrowPosition: arrowPosition)
 80 |   tipView.show()
 81 | 
82 |

Example

83 | 84 |

To run the example project, clone the repo, and run pod install from the Example directory first.

85 |

License

86 | 87 |

NeatTipView is available under the MIT license. See the LICENSE file for more info.

88 |

Credits

89 | 90 |

NeatTipView is maintained by Rootstrap with the help of our contributors.

91 | 92 |

93 | 94 |
95 |
96 | 100 |
101 |
102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/Documents/search.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/docs/docsets/NeatTipView.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/NeatTipView.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/docs/docsets/NeatTipView.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/docs/img/gh.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | NeatTipView Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |

NeatTipView Docs (0% documented)

17 |
18 |
19 |
20 | 25 |
26 |
27 | 31 |
32 |
33 |
34 | 35 |

NeatTipView

36 | 37 |

Version 38 | License 39 | Platform

40 |

What is it?

41 | 42 |

NeatTipView allows you to display message tooltips that can be used as call to actions or informative tips.

43 | 44 |
    45 |
  • [x] Allows Different tip positionings.
  • 46 |
  • [x] Multiple animation styles.
  • 47 |
  • [x] Smart placement for dynamic strings.
  • 48 |
  • [x] Full NSAttributtedString support.
  • 49 |
50 |

Installation

51 | 52 |

NeatTipView is available through CocoaPods and Carthage.

53 |

Cocoapods

54 | 55 |

To install it, simply add the following line to your Podfile:

56 |
pod "NeatTipView"
 57 | 
58 |

Carthage

59 | 60 |

To install it, simply add the following line to your Cartfile:

61 |
github "rootstrap/NeatTipView"
 62 | 
63 |

Usage

64 |

1. Customize your preferences

65 | 66 |

Preferences are encapsulated inside the NeatViewPreferences struct, check the inline docs for more info about which customization points are available.

67 | 68 |

Example:

69 |
  var preferences = NeatViewPreferences()
 70 |   preferences.animationPreferences.appearanceAnimationType = .fromBottom
 71 |   preferences.animationPreferences.disappearanceAnimationType = .toBottom
 72 | 
73 |

2. Initialize and Present the tip

74 |
  let tipView = NeatTipView(
 75 |     superview: view,
 76 |     centerPoint: center,
 77 |     attributedString: attributedString(),
 78 |                             preferences: preferences,
 79 |                             arrowPosition: arrowPosition)
 80 |   tipView.show()
 81 | 
82 |

Example

83 | 84 |

To run the example project, clone the repo, and run pod install from the Example directory first.

85 |

License

86 | 87 |

NeatTipView is available under the MIT license. See the LICENSE file for more info.

88 |

Credits

89 | 90 |

NeatTipView is maintained by Rootstrap with the help of our contributors.

91 | 92 |

93 | 94 |
95 |
96 | 100 |
101 |
102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /docs/search.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /docs/undocumented.json: -------------------------------------------------------------------------------- 1 | { 2 | "warnings": [ 3 | 4 | ], 5 | "source_directory": "/Users/mauriciocousillas/projects/NeatTipView" 6 | } -------------------------------------------------------------------------------- /generate-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/(shell) 2 | 3 | jazzy \ 4 | --clean \ 5 | --author Rootstrap \ 6 | --author_url https://rootstrap.com \ 7 | --github_url https://github.com/rootstrap/NeatTipView \ 8 | --github-file-prefix https://github.com/rootstrap/NeatTipView \ 9 | --module-version 1.0.0 \ 10 | --output docs/swift_output \ 11 | --theme docs/themes 12 | -------------------------------------------------------------------------------- /tipViewsExample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/NeatTipView/63f0b5b860fc3ff47632322bb3a557d600bbb58b/tipViewsExample.gif --------------------------------------------------------------------------------