├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── .travis.yml
├── EasyTipView.podspec
├── EasyTipView.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ └── EasyTipView.xcscheme
├── EasyTipView.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── Example
├── EasyTipView-Example.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── EasyTipView-Example.xcscheme
└── EasyTipView
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ ├── LaunchScreen.xib
│ └── Main.storyboard
│ ├── Images.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── EasyTipView copy 2-1.png
│ │ ├── EasyTipView copy 2.png
│ │ ├── EasyTipView copy 3-1.png
│ │ ├── EasyTipView copy 3.png
│ │ ├── EasyTipView copy 4.png
│ │ └── EasyTipView.png
│ ├── Contents.json
│ └── easytipview.imageset
│ │ ├── Contents.json
│ │ └── easytipview@2x.png
│ ├── Info.plist
│ └── ViewController.swift
├── LICENSE
├── Package.swift
├── README.md
├── Sources
└── EasyTipView
│ ├── EasyTipView.h
│ ├── EasyTipView.swift
│ ├── Info.plist
│ └── UIKitExtensions.swift
├── Tests
├── EasyTipViewTests
│ └── Tests.swift
└── Info.plist
└── assets
├── animation.gif
├── easytipview.gif
├── easytipview.png
└── static.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Finder
2 | .DS_Store
3 |
4 | # Xcode
5 | #
6 | build/
7 | *.pbxuser
8 | !default.pbxuser
9 | *.mode1v3
10 | !default.mode1v3
11 | *.mode2v3
12 | !default.mode2v3
13 | *.perspectivev3
14 | !default.perspectivev3
15 | xcuserdata
16 | *.xccheckout
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 | *.xcuserstate
22 |
23 | # CocoaPods
24 | #
25 | # We recommend against adding the Pods directory to your .gitignore. However
26 | # you should judge for yourself, the pros and cons are mentioned at:
27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
28 | #
29 | # Pods/
30 | .build/
31 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # references:
2 | # * http://www.objc.io/issue-6/travis-ci.html
3 | # * https://github.com/supermarin/xcpretty#usage
4 |
5 | language: swift
6 | osx_image: xcode12.2
7 | env:
8 | global:
9 | - WORKSPACE=EasyTipView.xcworkspace
10 | - FRAMEWORK_NAME=EasyTipView
11 | - PROJECT=EasyTipView.xcodeproj
12 | - SCHEME=EasyTipView
13 | before_deploy:
14 | - carthage build --no-skip-current
15 | - carthage archive $FRAMEWORK_NAME
16 | script: xcodebuild -scheme $SCHEME -workspace $WORKSPACE -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12 Pro' build test
17 |
--------------------------------------------------------------------------------
/EasyTipView.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'EasyTipView'
3 | s.version = '2.1.0'
4 | s.license = 'MIT'
5 | s.summary = 'Elegant tooltip view written in Swift'
6 | s.description = 'EasyTipView is a fully customisable tooltip view written in Swift that can be used as a call to action or informative tip. It can be shown above of below any UIBarItem or UIView subclass.'
7 | s.homepage = 'https://github.com/teodorpatras/EasyTipView'
8 | s.authors = { 'Teodor Patraș' => 'hello@teodorpatras.com' }
9 | s.source = { :git => 'https://github.com/teodorpatras/EasyTipView.git', :tag => s.version }
10 |
11 | s.swift_version = '5.0'
12 | s.ios.deployment_target = '10.0'
13 |
14 | s.source_files = 'Sources/EasyTipView/*.swift'
15 |
16 | s.requires_arc = true
17 | end
18 |
--------------------------------------------------------------------------------
/EasyTipView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1310EC961D0C53800000E71E /* EasyTipView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1310EC8B1D0C537F0000E71E /* EasyTipView.framework */; };
11 | 13FB32A91D0C53E5001ACE20 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13FB32A61D0C53E2001ACE20 /* Tests.swift */; };
12 | 3DEF6DAB23A39E4F007B8C3C /* EasyTipView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3DEF6DA823A39E4F007B8C3C /* EasyTipView.h */; settings = {ATTRIBUTES = (Public, ); }; };
13 | 3DEF6DAC23A39E4F007B8C3C /* UIKitExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DEF6DA923A39E4F007B8C3C /* UIKitExtensions.swift */; };
14 | 3DEF6DAD23A39E4F007B8C3C /* EasyTipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DEF6DAA23A39E4F007B8C3C /* EasyTipView.swift */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXContainerItemProxy section */
18 | 1310EC971D0C53800000E71E /* PBXContainerItemProxy */ = {
19 | isa = PBXContainerItemProxy;
20 | containerPortal = 1310EC821D0C537F0000E71E /* Project object */;
21 | proxyType = 1;
22 | remoteGlobalIDString = 1310EC8A1D0C537F0000E71E;
23 | remoteInfo = EasyTipView;
24 | };
25 | /* End PBXContainerItemProxy section */
26 |
27 | /* Begin PBXFileReference section */
28 | 1310EC8B1D0C537F0000E71E /* EasyTipView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = EasyTipView.framework; sourceTree = BUILT_PRODUCTS_DIR; };
29 | 1310EC951D0C53800000E71E /* EasyTipViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EasyTipViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
30 | 13FB32A11D0C53CB001ACE20 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Sources/EasyTipView/info.plist; sourceTree = SOURCE_ROOT; };
31 | 13FB32A51D0C53E2001ACE20 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Tests/Info.plist; sourceTree = SOURCE_ROOT; };
32 | 13FB32A61D0C53E2001ACE20 /* Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Tests.swift; path = Tests/EasyTipViewTests/Tests.swift; sourceTree = SOURCE_ROOT; };
33 | 3DEF6DA823A39E4F007B8C3C /* EasyTipView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EasyTipView.h; sourceTree = ""; };
34 | 3DEF6DA923A39E4F007B8C3C /* UIKitExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKitExtensions.swift; sourceTree = ""; };
35 | 3DEF6DAA23A39E4F007B8C3C /* EasyTipView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EasyTipView.swift; sourceTree = ""; };
36 | /* End PBXFileReference section */
37 |
38 | /* Begin PBXFrameworksBuildPhase section */
39 | 1310EC871D0C537F0000E71E /* Frameworks */ = {
40 | isa = PBXFrameworksBuildPhase;
41 | buildActionMask = 2147483647;
42 | files = (
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | 1310EC921D0C53800000E71E /* Frameworks */ = {
47 | isa = PBXFrameworksBuildPhase;
48 | buildActionMask = 2147483647;
49 | files = (
50 | 1310EC961D0C53800000E71E /* EasyTipView.framework in Frameworks */,
51 | );
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | /* End PBXFrameworksBuildPhase section */
55 |
56 | /* Begin PBXGroup section */
57 | 1310EC811D0C537F0000E71E = {
58 | isa = PBXGroup;
59 | children = (
60 | 1310EC8D1D0C537F0000E71E /* Sources */,
61 | 1310EC991D0C53800000E71E /* Tests */,
62 | 1310EC8C1D0C537F0000E71E /* Products */,
63 | );
64 | sourceTree = "";
65 | };
66 | 1310EC8C1D0C537F0000E71E /* Products */ = {
67 | isa = PBXGroup;
68 | children = (
69 | 1310EC8B1D0C537F0000E71E /* EasyTipView.framework */,
70 | 1310EC951D0C53800000E71E /* EasyTipViewTests.xctest */,
71 | );
72 | name = Products;
73 | sourceTree = "";
74 | };
75 | 1310EC8D1D0C537F0000E71E /* Sources */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 3DEF6DA623A39E20007B8C3C /* EasyTipView */,
79 | 13FB32A11D0C53CB001ACE20 /* Info.plist */,
80 | );
81 | path = Sources;
82 | sourceTree = "";
83 | };
84 | 1310EC991D0C53800000E71E /* Tests */ = {
85 | isa = PBXGroup;
86 | children = (
87 | 13FB32A51D0C53E2001ACE20 /* Info.plist */,
88 | 3DEF6DA723A39E3E007B8C3C /* EasyTipViewTests */,
89 | );
90 | path = Tests;
91 | sourceTree = "";
92 | };
93 | 3DEF6DA623A39E20007B8C3C /* EasyTipView */ = {
94 | isa = PBXGroup;
95 | children = (
96 | 3DEF6DA823A39E4F007B8C3C /* EasyTipView.h */,
97 | 3DEF6DAA23A39E4F007B8C3C /* EasyTipView.swift */,
98 | 3DEF6DA923A39E4F007B8C3C /* UIKitExtensions.swift */,
99 | );
100 | path = EasyTipView;
101 | sourceTree = "";
102 | };
103 | 3DEF6DA723A39E3E007B8C3C /* EasyTipViewTests */ = {
104 | isa = PBXGroup;
105 | children = (
106 | 13FB32A61D0C53E2001ACE20 /* Tests.swift */,
107 | );
108 | path = EasyTipViewTests;
109 | sourceTree = "";
110 | };
111 | /* End PBXGroup section */
112 |
113 | /* Begin PBXHeadersBuildPhase section */
114 | 1310EC881D0C537F0000E71E /* Headers */ = {
115 | isa = PBXHeadersBuildPhase;
116 | buildActionMask = 2147483647;
117 | files = (
118 | 3DEF6DAB23A39E4F007B8C3C /* EasyTipView.h in Headers */,
119 | );
120 | runOnlyForDeploymentPostprocessing = 0;
121 | };
122 | /* End PBXHeadersBuildPhase section */
123 |
124 | /* Begin PBXNativeTarget section */
125 | 1310EC8A1D0C537F0000E71E /* EasyTipView */ = {
126 | isa = PBXNativeTarget;
127 | buildConfigurationList = 1310EC9F1D0C53800000E71E /* Build configuration list for PBXNativeTarget "EasyTipView" */;
128 | buildPhases = (
129 | 1310EC861D0C537F0000E71E /* Sources */,
130 | 1310EC871D0C537F0000E71E /* Frameworks */,
131 | 1310EC881D0C537F0000E71E /* Headers */,
132 | 1310EC891D0C537F0000E71E /* Resources */,
133 | );
134 | buildRules = (
135 | );
136 | dependencies = (
137 | );
138 | name = EasyTipView;
139 | productName = EasyTipView;
140 | productReference = 1310EC8B1D0C537F0000E71E /* EasyTipView.framework */;
141 | productType = "com.apple.product-type.framework";
142 | };
143 | 1310EC941D0C53800000E71E /* EasyTipViewTests */ = {
144 | isa = PBXNativeTarget;
145 | buildConfigurationList = 1310ECA21D0C53800000E71E /* Build configuration list for PBXNativeTarget "EasyTipViewTests" */;
146 | buildPhases = (
147 | 1310EC911D0C53800000E71E /* Sources */,
148 | 1310EC921D0C53800000E71E /* Frameworks */,
149 | 1310EC931D0C53800000E71E /* Resources */,
150 | );
151 | buildRules = (
152 | );
153 | dependencies = (
154 | 1310EC981D0C53800000E71E /* PBXTargetDependency */,
155 | );
156 | name = EasyTipViewTests;
157 | productName = EasyTipViewTests;
158 | productReference = 1310EC951D0C53800000E71E /* EasyTipViewTests.xctest */;
159 | productType = "com.apple.product-type.bundle.unit-test";
160 | };
161 | /* End PBXNativeTarget section */
162 |
163 | /* Begin PBXProject section */
164 | 1310EC821D0C537F0000E71E /* Project object */ = {
165 | isa = PBXProject;
166 | attributes = {
167 | LastSwiftUpdateCheck = 0730;
168 | LastUpgradeCheck = 1020;
169 | ORGANIZATIONNAME = teodorpatras;
170 | TargetAttributes = {
171 | 1310EC8A1D0C537F0000E71E = {
172 | CreatedOnToolsVersion = 7.3.1;
173 | LastSwiftMigration = 1220;
174 | ProvisioningStyle = Automatic;
175 | };
176 | 1310EC941D0C53800000E71E = {
177 | CreatedOnToolsVersion = 7.3.1;
178 | LastSwiftMigration = 1220;
179 | };
180 | };
181 | };
182 | buildConfigurationList = 1310EC851D0C537F0000E71E /* Build configuration list for PBXProject "EasyTipView" */;
183 | compatibilityVersion = "Xcode 3.2";
184 | developmentRegion = en;
185 | hasScannedForEncodings = 0;
186 | knownRegions = (
187 | en,
188 | Base,
189 | );
190 | mainGroup = 1310EC811D0C537F0000E71E;
191 | productRefGroup = 1310EC8C1D0C537F0000E71E /* Products */;
192 | projectDirPath = "";
193 | projectRoot = "";
194 | targets = (
195 | 1310EC8A1D0C537F0000E71E /* EasyTipView */,
196 | 1310EC941D0C53800000E71E /* EasyTipViewTests */,
197 | );
198 | };
199 | /* End PBXProject section */
200 |
201 | /* Begin PBXResourcesBuildPhase section */
202 | 1310EC891D0C537F0000E71E /* Resources */ = {
203 | isa = PBXResourcesBuildPhase;
204 | buildActionMask = 2147483647;
205 | files = (
206 | );
207 | runOnlyForDeploymentPostprocessing = 0;
208 | };
209 | 1310EC931D0C53800000E71E /* Resources */ = {
210 | isa = PBXResourcesBuildPhase;
211 | buildActionMask = 2147483647;
212 | files = (
213 | );
214 | runOnlyForDeploymentPostprocessing = 0;
215 | };
216 | /* End PBXResourcesBuildPhase section */
217 |
218 | /* Begin PBXSourcesBuildPhase section */
219 | 1310EC861D0C537F0000E71E /* Sources */ = {
220 | isa = PBXSourcesBuildPhase;
221 | buildActionMask = 2147483647;
222 | files = (
223 | 3DEF6DAD23A39E4F007B8C3C /* EasyTipView.swift in Sources */,
224 | 3DEF6DAC23A39E4F007B8C3C /* UIKitExtensions.swift in Sources */,
225 | );
226 | runOnlyForDeploymentPostprocessing = 0;
227 | };
228 | 1310EC911D0C53800000E71E /* Sources */ = {
229 | isa = PBXSourcesBuildPhase;
230 | buildActionMask = 2147483647;
231 | files = (
232 | 13FB32A91D0C53E5001ACE20 /* Tests.swift in Sources */,
233 | );
234 | runOnlyForDeploymentPostprocessing = 0;
235 | };
236 | /* End PBXSourcesBuildPhase section */
237 |
238 | /* Begin PBXTargetDependency section */
239 | 1310EC981D0C53800000E71E /* PBXTargetDependency */ = {
240 | isa = PBXTargetDependency;
241 | target = 1310EC8A1D0C537F0000E71E /* EasyTipView */;
242 | targetProxy = 1310EC971D0C53800000E71E /* PBXContainerItemProxy */;
243 | };
244 | /* End PBXTargetDependency section */
245 |
246 | /* Begin XCBuildConfiguration section */
247 | 1310EC9D1D0C53800000E71E /* Debug */ = {
248 | isa = XCBuildConfiguration;
249 | buildSettings = {
250 | ALWAYS_SEARCH_USER_PATHS = NO;
251 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
252 | CLANG_ANALYZER_NONNULL = YES;
253 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
254 | CLANG_CXX_LIBRARY = "libc++";
255 | CLANG_ENABLE_MODULES = YES;
256 | CLANG_ENABLE_OBJC_ARC = YES;
257 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
258 | CLANG_WARN_BOOL_CONVERSION = YES;
259 | CLANG_WARN_COMMA = YES;
260 | CLANG_WARN_CONSTANT_CONVERSION = YES;
261 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
262 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
263 | CLANG_WARN_EMPTY_BODY = YES;
264 | CLANG_WARN_ENUM_CONVERSION = YES;
265 | CLANG_WARN_INFINITE_RECURSION = YES;
266 | CLANG_WARN_INT_CONVERSION = YES;
267 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
268 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
269 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
270 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
271 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
272 | CLANG_WARN_STRICT_PROTOTYPES = YES;
273 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
274 | CLANG_WARN_UNREACHABLE_CODE = YES;
275 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
276 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
277 | COPY_PHASE_STRIP = NO;
278 | CURRENT_PROJECT_VERSION = 1;
279 | DEBUG_INFORMATION_FORMAT = dwarf;
280 | ENABLE_STRICT_OBJC_MSGSEND = YES;
281 | ENABLE_TESTABILITY = YES;
282 | GCC_C_LANGUAGE_STANDARD = gnu99;
283 | GCC_DYNAMIC_NO_PIC = NO;
284 | GCC_NO_COMMON_BLOCKS = YES;
285 | GCC_OPTIMIZATION_LEVEL = 0;
286 | GCC_PREPROCESSOR_DEFINITIONS = (
287 | "DEBUG=1",
288 | "$(inherited)",
289 | );
290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
292 | GCC_WARN_UNDECLARED_SELECTOR = YES;
293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
294 | GCC_WARN_UNUSED_FUNCTION = YES;
295 | GCC_WARN_UNUSED_VARIABLE = YES;
296 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
297 | MTL_ENABLE_DEBUG_INFO = YES;
298 | ONLY_ACTIVE_ARCH = YES;
299 | SDKROOT = iphoneos;
300 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
301 | TARGETED_DEVICE_FAMILY = "1,2";
302 | VERSIONING_SYSTEM = "apple-generic";
303 | VERSION_INFO_PREFIX = "";
304 | };
305 | name = Debug;
306 | };
307 | 1310EC9E1D0C53800000E71E /* Release */ = {
308 | isa = XCBuildConfiguration;
309 | buildSettings = {
310 | ALWAYS_SEARCH_USER_PATHS = NO;
311 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
312 | CLANG_ANALYZER_NONNULL = YES;
313 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
314 | CLANG_CXX_LIBRARY = "libc++";
315 | CLANG_ENABLE_MODULES = YES;
316 | CLANG_ENABLE_OBJC_ARC = YES;
317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
318 | CLANG_WARN_BOOL_CONVERSION = YES;
319 | CLANG_WARN_COMMA = YES;
320 | CLANG_WARN_CONSTANT_CONVERSION = YES;
321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
323 | CLANG_WARN_EMPTY_BODY = YES;
324 | CLANG_WARN_ENUM_CONVERSION = YES;
325 | CLANG_WARN_INFINITE_RECURSION = YES;
326 | CLANG_WARN_INT_CONVERSION = YES;
327 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
328 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
329 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
330 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
331 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
332 | CLANG_WARN_STRICT_PROTOTYPES = YES;
333 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
334 | CLANG_WARN_UNREACHABLE_CODE = YES;
335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
336 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
337 | COPY_PHASE_STRIP = NO;
338 | CURRENT_PROJECT_VERSION = 1;
339 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
340 | ENABLE_NS_ASSERTIONS = NO;
341 | ENABLE_STRICT_OBJC_MSGSEND = YES;
342 | GCC_C_LANGUAGE_STANDARD = gnu99;
343 | GCC_NO_COMMON_BLOCKS = YES;
344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
346 | GCC_WARN_UNDECLARED_SELECTOR = YES;
347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
348 | GCC_WARN_UNUSED_FUNCTION = YES;
349 | GCC_WARN_UNUSED_VARIABLE = YES;
350 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
351 | MTL_ENABLE_DEBUG_INFO = NO;
352 | SDKROOT = iphoneos;
353 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
354 | TARGETED_DEVICE_FAMILY = "1,2";
355 | VALIDATE_PRODUCT = YES;
356 | VERSIONING_SYSTEM = "apple-generic";
357 | VERSION_INFO_PREFIX = "";
358 | };
359 | name = Release;
360 | };
361 | 1310ECA01D0C53800000E71E /* Debug */ = {
362 | isa = XCBuildConfiguration;
363 | buildSettings = {
364 | CLANG_ENABLE_MODULES = YES;
365 | CODE_SIGN_IDENTITY = "iPhone Developer";
366 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
367 | DEFINES_MODULE = YES;
368 | DEVELOPMENT_TEAM = "";
369 | DYLIB_COMPATIBILITY_VERSION = 1;
370 | DYLIB_CURRENT_VERSION = 1;
371 | DYLIB_INSTALL_NAME_BASE = "@rpath";
372 | INFOPLIST_FILE = Sources/EasyTipView/info.plist;
373 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
374 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
375 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
376 | PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.EasyTipView;
377 | PRODUCT_NAME = "$(TARGET_NAME)";
378 | SKIP_INSTALL = YES;
379 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
380 | SWIFT_VERSION = 5.0;
381 | };
382 | name = Debug;
383 | };
384 | 1310ECA11D0C53800000E71E /* Release */ = {
385 | isa = XCBuildConfiguration;
386 | buildSettings = {
387 | CLANG_ENABLE_MODULES = YES;
388 | CODE_SIGN_IDENTITY = "iPhone Developer";
389 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
390 | DEFINES_MODULE = YES;
391 | DEVELOPMENT_TEAM = "";
392 | DYLIB_COMPATIBILITY_VERSION = 1;
393 | DYLIB_CURRENT_VERSION = 1;
394 | DYLIB_INSTALL_NAME_BASE = "@rpath";
395 | INFOPLIST_FILE = Sources/EasyTipView/info.plist;
396 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
397 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
398 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
399 | PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.EasyTipView;
400 | PRODUCT_NAME = "$(TARGET_NAME)";
401 | SKIP_INSTALL = YES;
402 | SWIFT_VERSION = 5.0;
403 | };
404 | name = Release;
405 | };
406 | 1310ECA31D0C53800000E71E /* Debug */ = {
407 | isa = XCBuildConfiguration;
408 | buildSettings = {
409 | INFOPLIST_FILE = Tests/Info.plist;
410 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
411 | PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.EasyTipViewTests;
412 | PRODUCT_NAME = "$(TARGET_NAME)";
413 | SWIFT_VERSION = 5.0;
414 | };
415 | name = Debug;
416 | };
417 | 1310ECA41D0C53800000E71E /* Release */ = {
418 | isa = XCBuildConfiguration;
419 | buildSettings = {
420 | INFOPLIST_FILE = Tests/Info.plist;
421 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
422 | PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.EasyTipViewTests;
423 | PRODUCT_NAME = "$(TARGET_NAME)";
424 | SWIFT_VERSION = 5.0;
425 | };
426 | name = Release;
427 | };
428 | /* End XCBuildConfiguration section */
429 |
430 | /* Begin XCConfigurationList section */
431 | 1310EC851D0C537F0000E71E /* Build configuration list for PBXProject "EasyTipView" */ = {
432 | isa = XCConfigurationList;
433 | buildConfigurations = (
434 | 1310EC9D1D0C53800000E71E /* Debug */,
435 | 1310EC9E1D0C53800000E71E /* Release */,
436 | );
437 | defaultConfigurationIsVisible = 0;
438 | defaultConfigurationName = Release;
439 | };
440 | 1310EC9F1D0C53800000E71E /* Build configuration list for PBXNativeTarget "EasyTipView" */ = {
441 | isa = XCConfigurationList;
442 | buildConfigurations = (
443 | 1310ECA01D0C53800000E71E /* Debug */,
444 | 1310ECA11D0C53800000E71E /* Release */,
445 | );
446 | defaultConfigurationIsVisible = 0;
447 | defaultConfigurationName = Release;
448 | };
449 | 1310ECA21D0C53800000E71E /* Build configuration list for PBXNativeTarget "EasyTipViewTests" */ = {
450 | isa = XCConfigurationList;
451 | buildConfigurations = (
452 | 1310ECA31D0C53800000E71E /* Debug */,
453 | 1310ECA41D0C53800000E71E /* Release */,
454 | );
455 | defaultConfigurationIsVisible = 0;
456 | defaultConfigurationName = Release;
457 | };
458 | /* End XCConfigurationList section */
459 | };
460 | rootObject = 1310EC821D0C537F0000E71E /* Project object */;
461 | }
462 |
--------------------------------------------------------------------------------
/EasyTipView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/EasyTipView.xcodeproj/xcshareddata/xcschemes/EasyTipView.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/EasyTipView.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/EasyTipView.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/EasyTipView-Example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1304D4D61D0C549300DC945E /* EasyTipView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1304D4D31D0C548900DC945E /* EasyTipView.framework */; };
11 | 1304D4D71D0C549300DC945E /* EasyTipView.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 1304D4D31D0C548900DC945E /* EasyTipView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
12 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
13 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; };
14 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
15 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; };
16 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXContainerItemProxy section */
20 | 1304D4D21D0C548900DC945E /* PBXContainerItemProxy */ = {
21 | isa = PBXContainerItemProxy;
22 | containerPortal = 1304D4CD1D0C548900DC945E /* EasyTipView.xcodeproj */;
23 | proxyType = 2;
24 | remoteGlobalIDString = 1310EC8B1D0C537F0000E71E;
25 | remoteInfo = EasyTipView;
26 | };
27 | 1304D4D41D0C548900DC945E /* PBXContainerItemProxy */ = {
28 | isa = PBXContainerItemProxy;
29 | containerPortal = 1304D4CD1D0C548900DC945E /* EasyTipView.xcodeproj */;
30 | proxyType = 2;
31 | remoteGlobalIDString = 1310EC951D0C53800000E71E;
32 | remoteInfo = EasyTipViewTests;
33 | };
34 | 1304D4D81D0C549300DC945E /* PBXContainerItemProxy */ = {
35 | isa = PBXContainerItemProxy;
36 | containerPortal = 1304D4CD1D0C548900DC945E /* EasyTipView.xcodeproj */;
37 | proxyType = 1;
38 | remoteGlobalIDString = 1310EC8A1D0C537F0000E71E;
39 | remoteInfo = EasyTipView;
40 | };
41 | /* End PBXContainerItemProxy section */
42 |
43 | /* Begin PBXCopyFilesBuildPhase section */
44 | 1304D4DA1D0C549300DC945E /* Embed Frameworks */ = {
45 | isa = PBXCopyFilesBuildPhase;
46 | buildActionMask = 2147483647;
47 | dstPath = "";
48 | dstSubfolderSpec = 10;
49 | files = (
50 | 1304D4D71D0C549300DC945E /* EasyTipView.framework in Embed Frameworks */,
51 | );
52 | name = "Embed Frameworks";
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXCopyFilesBuildPhase section */
56 |
57 | /* Begin PBXFileReference section */
58 | 1304D4CD1D0C548900DC945E /* EasyTipView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = EasyTipView.xcodeproj; path = ../EasyTipView.xcodeproj; sourceTree = ""; };
59 | 25C0D45918694E34370CD90F /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; };
60 | 607FACD01AFB9204008FA782 /* EasyTipView-Example_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "EasyTipView-Example_Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
61 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
62 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
63 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
64 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
65 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
66 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; };
67 | A4634EACF3352E09D72EE057 /* EasyTipView.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = EasyTipView.podspec; path = ../EasyTipView.podspec; sourceTree = ""; };
68 | B009D6802E476C56C927E400 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; };
69 | /* End PBXFileReference section */
70 |
71 | /* Begin PBXFrameworksBuildPhase section */
72 | 607FACCD1AFB9204008FA782 /* Frameworks */ = {
73 | isa = PBXFrameworksBuildPhase;
74 | buildActionMask = 2147483647;
75 | files = (
76 | 1304D4D61D0C549300DC945E /* EasyTipView.framework in Frameworks */,
77 | );
78 | runOnlyForDeploymentPostprocessing = 0;
79 | };
80 | /* End PBXFrameworksBuildPhase section */
81 |
82 | /* Begin PBXGroup section */
83 | 1304D4CE1D0C548900DC945E /* Products */ = {
84 | isa = PBXGroup;
85 | children = (
86 | 1304D4D31D0C548900DC945E /* EasyTipView.framework */,
87 | 1304D4D51D0C548900DC945E /* EasyTipViewTests.xctest */,
88 | );
89 | name = Products;
90 | sourceTree = "";
91 | };
92 | 607FACC71AFB9204008FA782 = {
93 | isa = PBXGroup;
94 | children = (
95 | 1304D4CD1D0C548900DC945E /* EasyTipView.xcodeproj */,
96 | 607FACF51AFB993E008FA782 /* Podspec Metadata */,
97 | 607FACD21AFB9204008FA782 /* Example for EasyTipView */,
98 | 607FACD11AFB9204008FA782 /* Products */,
99 | );
100 | sourceTree = "";
101 | };
102 | 607FACD11AFB9204008FA782 /* Products */ = {
103 | isa = PBXGroup;
104 | children = (
105 | 607FACD01AFB9204008FA782 /* EasyTipView-Example_Example.app */,
106 | );
107 | name = Products;
108 | sourceTree = "";
109 | };
110 | 607FACD21AFB9204008FA782 /* Example for EasyTipView */ = {
111 | isa = PBXGroup;
112 | children = (
113 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */,
114 | 607FACD71AFB9204008FA782 /* ViewController.swift */,
115 | 607FACD91AFB9204008FA782 /* Main.storyboard */,
116 | 607FACDC1AFB9204008FA782 /* Images.xcassets */,
117 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
118 | 607FACD31AFB9204008FA782 /* Supporting Files */,
119 | );
120 | name = "Example for EasyTipView";
121 | path = EasyTipView;
122 | sourceTree = "";
123 | };
124 | 607FACD31AFB9204008FA782 /* Supporting Files */ = {
125 | isa = PBXGroup;
126 | children = (
127 | 607FACD41AFB9204008FA782 /* Info.plist */,
128 | );
129 | name = "Supporting Files";
130 | sourceTree = "";
131 | };
132 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = {
133 | isa = PBXGroup;
134 | children = (
135 | A4634EACF3352E09D72EE057 /* EasyTipView.podspec */,
136 | 25C0D45918694E34370CD90F /* README.md */,
137 | B009D6802E476C56C927E400 /* LICENSE */,
138 | );
139 | name = "Podspec Metadata";
140 | sourceTree = "";
141 | };
142 | /* End PBXGroup section */
143 |
144 | /* Begin PBXNativeTarget section */
145 | 607FACCF1AFB9204008FA782 /* EasyTipView-Example */ = {
146 | isa = PBXNativeTarget;
147 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "EasyTipView-Example" */;
148 | buildPhases = (
149 | 607FACCC1AFB9204008FA782 /* Sources */,
150 | 607FACCD1AFB9204008FA782 /* Frameworks */,
151 | 607FACCE1AFB9204008FA782 /* Resources */,
152 | 1304D4DA1D0C549300DC945E /* Embed Frameworks */,
153 | );
154 | buildRules = (
155 | );
156 | dependencies = (
157 | 1304D4D91D0C549300DC945E /* PBXTargetDependency */,
158 | );
159 | name = "EasyTipView-Example";
160 | productName = EasyTipView;
161 | productReference = 607FACD01AFB9204008FA782 /* EasyTipView-Example_Example.app */;
162 | productType = "com.apple.product-type.application";
163 | };
164 | /* End PBXNativeTarget section */
165 |
166 | /* Begin PBXProject section */
167 | 607FACC81AFB9204008FA782 /* Project object */ = {
168 | isa = PBXProject;
169 | attributes = {
170 | LastSwiftMigration = 0700;
171 | LastSwiftUpdateCheck = 0700;
172 | LastUpgradeCheck = 1020;
173 | ORGANIZATIONNAME = CocoaPods;
174 | TargetAttributes = {
175 | 607FACCF1AFB9204008FA782 = {
176 | CreatedOnToolsVersion = 6.3.1;
177 | DevelopmentTeam = 752CFWL5Y4;
178 | LastSwiftMigration = 0930;
179 | };
180 | };
181 | };
182 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "EasyTipView-Example" */;
183 | compatibilityVersion = "Xcode 3.2";
184 | developmentRegion = en;
185 | hasScannedForEncodings = 0;
186 | knownRegions = (
187 | en,
188 | Base,
189 | );
190 | mainGroup = 607FACC71AFB9204008FA782;
191 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */;
192 | projectDirPath = "";
193 | projectReferences = (
194 | {
195 | ProductGroup = 1304D4CE1D0C548900DC945E /* Products */;
196 | ProjectRef = 1304D4CD1D0C548900DC945E /* EasyTipView.xcodeproj */;
197 | },
198 | );
199 | projectRoot = "";
200 | targets = (
201 | 607FACCF1AFB9204008FA782 /* EasyTipView-Example */,
202 | );
203 | };
204 | /* End PBXProject section */
205 |
206 | /* Begin PBXReferenceProxy section */
207 | 1304D4D31D0C548900DC945E /* EasyTipView.framework */ = {
208 | isa = PBXReferenceProxy;
209 | fileType = wrapper.framework;
210 | path = EasyTipView.framework;
211 | remoteRef = 1304D4D21D0C548900DC945E /* PBXContainerItemProxy */;
212 | sourceTree = BUILT_PRODUCTS_DIR;
213 | };
214 | 1304D4D51D0C548900DC945E /* EasyTipViewTests.xctest */ = {
215 | isa = PBXReferenceProxy;
216 | fileType = wrapper.cfbundle;
217 | path = EasyTipViewTests.xctest;
218 | remoteRef = 1304D4D41D0C548900DC945E /* PBXContainerItemProxy */;
219 | sourceTree = BUILT_PRODUCTS_DIR;
220 | };
221 | /* End PBXReferenceProxy section */
222 |
223 | /* Begin PBXResourcesBuildPhase section */
224 | 607FACCE1AFB9204008FA782 /* Resources */ = {
225 | isa = PBXResourcesBuildPhase;
226 | buildActionMask = 2147483647;
227 | files = (
228 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */,
229 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */,
230 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */,
231 | );
232 | runOnlyForDeploymentPostprocessing = 0;
233 | };
234 | /* End PBXResourcesBuildPhase section */
235 |
236 | /* Begin PBXSourcesBuildPhase section */
237 | 607FACCC1AFB9204008FA782 /* Sources */ = {
238 | isa = PBXSourcesBuildPhase;
239 | buildActionMask = 2147483647;
240 | files = (
241 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */,
242 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */,
243 | );
244 | runOnlyForDeploymentPostprocessing = 0;
245 | };
246 | /* End PBXSourcesBuildPhase section */
247 |
248 | /* Begin PBXTargetDependency section */
249 | 1304D4D91D0C549300DC945E /* PBXTargetDependency */ = {
250 | isa = PBXTargetDependency;
251 | name = EasyTipView;
252 | targetProxy = 1304D4D81D0C549300DC945E /* PBXContainerItemProxy */;
253 | };
254 | /* End PBXTargetDependency section */
255 |
256 | /* Begin PBXVariantGroup section */
257 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = {
258 | isa = PBXVariantGroup;
259 | children = (
260 | 607FACDA1AFB9204008FA782 /* Base */,
261 | );
262 | name = Main.storyboard;
263 | sourceTree = "";
264 | };
265 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = {
266 | isa = PBXVariantGroup;
267 | children = (
268 | 607FACDF1AFB9204008FA782 /* Base */,
269 | );
270 | name = LaunchScreen.xib;
271 | sourceTree = "";
272 | };
273 | /* End PBXVariantGroup section */
274 |
275 | /* Begin XCBuildConfiguration section */
276 | 607FACED1AFB9204008FA782 /* Debug */ = {
277 | isa = XCBuildConfiguration;
278 | buildSettings = {
279 | ALWAYS_SEARCH_USER_PATHS = NO;
280 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
281 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
282 | CLANG_CXX_LIBRARY = "libc++";
283 | CLANG_ENABLE_MODULES = YES;
284 | CLANG_ENABLE_OBJC_ARC = YES;
285 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
286 | CLANG_WARN_BOOL_CONVERSION = YES;
287 | CLANG_WARN_COMMA = YES;
288 | CLANG_WARN_CONSTANT_CONVERSION = YES;
289 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
290 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
291 | CLANG_WARN_EMPTY_BODY = YES;
292 | CLANG_WARN_ENUM_CONVERSION = YES;
293 | CLANG_WARN_INFINITE_RECURSION = YES;
294 | CLANG_WARN_INT_CONVERSION = YES;
295 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
296 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
297 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
298 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
299 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
300 | CLANG_WARN_STRICT_PROTOTYPES = YES;
301 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
302 | CLANG_WARN_UNREACHABLE_CODE = YES;
303 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
304 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
305 | COPY_PHASE_STRIP = NO;
306 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
307 | ENABLE_STRICT_OBJC_MSGSEND = YES;
308 | ENABLE_TESTABILITY = YES;
309 | GCC_C_LANGUAGE_STANDARD = gnu99;
310 | GCC_DYNAMIC_NO_PIC = NO;
311 | GCC_NO_COMMON_BLOCKS = YES;
312 | GCC_OPTIMIZATION_LEVEL = 0;
313 | GCC_PREPROCESSOR_DEFINITIONS = (
314 | "DEBUG=1",
315 | "$(inherited)",
316 | );
317 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
318 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
319 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
320 | GCC_WARN_UNDECLARED_SELECTOR = YES;
321 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
322 | GCC_WARN_UNUSED_FUNCTION = YES;
323 | GCC_WARN_UNUSED_VARIABLE = YES;
324 | IPHONEOS_DEPLOYMENT_TARGET = 8.3;
325 | MTL_ENABLE_DEBUG_INFO = YES;
326 | ONLY_ACTIVE_ARCH = YES;
327 | SDKROOT = iphoneos;
328 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
329 | };
330 | name = Debug;
331 | };
332 | 607FACEE1AFB9204008FA782 /* Release */ = {
333 | isa = XCBuildConfiguration;
334 | buildSettings = {
335 | ALWAYS_SEARCH_USER_PATHS = NO;
336 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
337 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
338 | CLANG_CXX_LIBRARY = "libc++";
339 | CLANG_ENABLE_MODULES = YES;
340 | CLANG_ENABLE_OBJC_ARC = YES;
341 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
342 | CLANG_WARN_BOOL_CONVERSION = YES;
343 | CLANG_WARN_COMMA = YES;
344 | CLANG_WARN_CONSTANT_CONVERSION = YES;
345 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
346 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
347 | CLANG_WARN_EMPTY_BODY = YES;
348 | CLANG_WARN_ENUM_CONVERSION = YES;
349 | CLANG_WARN_INFINITE_RECURSION = YES;
350 | CLANG_WARN_INT_CONVERSION = YES;
351 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
352 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
353 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
354 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
355 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
356 | CLANG_WARN_STRICT_PROTOTYPES = YES;
357 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
358 | CLANG_WARN_UNREACHABLE_CODE = YES;
359 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
360 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
361 | COPY_PHASE_STRIP = NO;
362 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
363 | ENABLE_NS_ASSERTIONS = NO;
364 | ENABLE_STRICT_OBJC_MSGSEND = YES;
365 | GCC_C_LANGUAGE_STANDARD = gnu99;
366 | GCC_NO_COMMON_BLOCKS = YES;
367 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
368 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
369 | GCC_WARN_UNDECLARED_SELECTOR = YES;
370 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
371 | GCC_WARN_UNUSED_FUNCTION = YES;
372 | GCC_WARN_UNUSED_VARIABLE = YES;
373 | IPHONEOS_DEPLOYMENT_TARGET = 8.3;
374 | MTL_ENABLE_DEBUG_INFO = NO;
375 | SDKROOT = iphoneos;
376 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
377 | VALIDATE_PRODUCT = YES;
378 | };
379 | name = Release;
380 | };
381 | 607FACF01AFB9204008FA782 /* Debug */ = {
382 | isa = XCBuildConfiguration;
383 | buildSettings = {
384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
385 | DEVELOPMENT_TEAM = 752CFWL5Y4;
386 | INFOPLIST_FILE = EasyTipView/Info.plist;
387 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
388 | MODULE_NAME = ExampleApp;
389 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.EasyTipView-demo";
390 | PRODUCT_NAME = "EasyTipView-Example_Example";
391 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
392 | SWIFT_VERSION = 4.0;
393 | };
394 | name = Debug;
395 | };
396 | 607FACF11AFB9204008FA782 /* Release */ = {
397 | isa = XCBuildConfiguration;
398 | buildSettings = {
399 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
400 | DEVELOPMENT_TEAM = 752CFWL5Y4;
401 | INFOPLIST_FILE = EasyTipView/Info.plist;
402 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
403 | MODULE_NAME = ExampleApp;
404 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.EasyTipView-demo";
405 | PRODUCT_NAME = "EasyTipView-Example_Example";
406 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
407 | SWIFT_VERSION = 4.0;
408 | };
409 | name = Release;
410 | };
411 | /* End XCBuildConfiguration section */
412 |
413 | /* Begin XCConfigurationList section */
414 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "EasyTipView-Example" */ = {
415 | isa = XCConfigurationList;
416 | buildConfigurations = (
417 | 607FACED1AFB9204008FA782 /* Debug */,
418 | 607FACEE1AFB9204008FA782 /* Release */,
419 | );
420 | defaultConfigurationIsVisible = 0;
421 | defaultConfigurationName = Release;
422 | };
423 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "EasyTipView-Example" */ = {
424 | isa = XCConfigurationList;
425 | buildConfigurations = (
426 | 607FACF01AFB9204008FA782 /* Debug */,
427 | 607FACF11AFB9204008FA782 /* Release */,
428 | );
429 | defaultConfigurationIsVisible = 0;
430 | defaultConfigurationName = Release;
431 | };
432 | /* End XCConfigurationList section */
433 | };
434 | rootObject = 607FACC81AFB9204008FA782 /* Project object */;
435 | }
436 |
--------------------------------------------------------------------------------
/Example/EasyTipView-Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/EasyTipView-Example.xcodeproj/xcshareddata/xcschemes/EasyTipView-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/EasyTipView/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // EasyTipView
4 | //
5 | // Created by Teodor Patraş on 08/16/2015.
6 | // Copyright (c) 2015 Teodor Patraş. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/Example/EasyTipView/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
24 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Example/EasyTipView/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
58 |
78 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
162 |
182 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
--------------------------------------------------------------------------------
/Example/EasyTipView/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 | "size" : "29x29",
15 | "idiom" : "iphone",
16 | "filename" : "EasyTipView.png",
17 | "scale" : "2x"
18 | },
19 | {
20 | "size" : "29x29",
21 | "idiom" : "iphone",
22 | "filename" : "EasyTipView copy 2-1.png",
23 | "scale" : "3x"
24 | },
25 | {
26 | "size" : "40x40",
27 | "idiom" : "iphone",
28 | "filename" : "EasyTipView copy 4.png",
29 | "scale" : "2x"
30 | },
31 | {
32 | "size" : "40x40",
33 | "idiom" : "iphone",
34 | "filename" : "EasyTipView copy 3.png",
35 | "scale" : "3x"
36 | },
37 | {
38 | "size" : "60x60",
39 | "idiom" : "iphone",
40 | "filename" : "EasyTipView copy 3-1.png",
41 | "scale" : "2x"
42 | },
43 | {
44 | "size" : "60x60",
45 | "idiom" : "iphone",
46 | "filename" : "EasyTipView copy 2.png",
47 | "scale" : "3x"
48 | },
49 | {
50 | "idiom" : "ios-marketing",
51 | "size" : "1024x1024",
52 | "scale" : "1x"
53 | }
54 | ],
55 | "info" : {
56 | "version" : 1,
57 | "author" : "xcode"
58 | }
59 | }
--------------------------------------------------------------------------------
/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 2-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 2-1.png
--------------------------------------------------------------------------------
/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 2.png
--------------------------------------------------------------------------------
/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 3-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 3-1.png
--------------------------------------------------------------------------------
/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 3.png
--------------------------------------------------------------------------------
/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView copy 4.png
--------------------------------------------------------------------------------
/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/Example/EasyTipView/Images.xcassets/AppIcon.appiconset/EasyTipView.png
--------------------------------------------------------------------------------
/Example/EasyTipView/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Example/EasyTipView/Images.xcassets/easytipview.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "easytipview@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Example/EasyTipView/Images.xcassets/easytipview.imageset/easytipview@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/Example/EasyTipView/Images.xcassets/easytipview.imageset/easytipview@2x.png
--------------------------------------------------------------------------------
/Example/EasyTipView/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 | UIInterfaceOrientationLandscapeRight
37 | UIInterfaceOrientationPortraitUpsideDown
38 | UIInterfaceOrientationLandscapeLeft
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Example/EasyTipView/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | //
4 | // Copyright (c) 2015 Teodor Patraş
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 |
24 | import UIKit
25 | import Darwin
26 | import EasyTipView
27 |
28 | class ViewController: UIViewController, EasyTipViewDelegate {
29 |
30 | @IBOutlet weak var toolbarItem: UIBarButtonItem!
31 | @IBOutlet weak var smallContainerView: UIView!
32 | @IBOutlet weak var navBarItem: UIBarButtonItem!
33 | @IBOutlet weak var buttonA: UIButton!
34 | @IBOutlet weak var buttonB: UIButton!
35 | @IBOutlet weak var buttonC: UIButton!
36 | @IBOutlet weak var buttonD: UIButton!
37 | @IBOutlet weak var buttonE: UIButton!
38 | @IBOutlet weak var buttonF: UIButton!
39 | @IBOutlet weak var buttonG: UIButton!
40 |
41 | weak var tipView: EasyTipView?
42 |
43 | override func viewDidLoad() {
44 | super.viewDidLoad()
45 |
46 | self.configureUI()
47 |
48 | var preferences = EasyTipView.Preferences()
49 |
50 | preferences.drawing.font = UIFont(name: "Futura-Medium", size: 13)!
51 | preferences.drawing.foregroundColor = UIColor.white
52 | preferences.drawing.backgroundColor = UIColor(hue:0.46, saturation:0.99, brightness:0.6, alpha:1)
53 |
54 | EasyTipView.globalPreferences = preferences
55 | self.view.backgroundColor = UIColor(hue:0.75, saturation:0.01, brightness:0.96, alpha:1.00)
56 | }
57 |
58 | override func viewDidAppear(_ animated: Bool) {
59 | super.viewDidAppear(animated)
60 | self.toolbarItemAction()
61 | }
62 |
63 | func easyTipViewDidTap(_ tipView: EasyTipView) {
64 | print("\(tipView) did tap!")
65 | }
66 |
67 | func easyTipViewDidDismiss(_ tipView: EasyTipView) {
68 | print("\(tipView) did dismiss!")
69 | }
70 |
71 | @IBAction func barButtonAction(sender: UIBarButtonItem) {
72 | let text = "Tip view for bar button item displayed within the navigation controller's view. Tap to dismiss."
73 | EasyTipView.show(forItem: self.navBarItem,
74 | withinSuperview: self.navigationController?.view,
75 | text: text,
76 | delegate : self)
77 | }
78 |
79 | @IBAction func toolbarItemAction() {
80 | if let tipView = tipView {
81 | tipView.dismiss(withCompletion: {
82 | print("Completion called!")
83 | })
84 | } else {
85 | let text = "EasyTipView is an easy to use tooltip view. It can point to any UIView or UIBarItem subclasses. Tap the buttons to see other tooltips."
86 |
87 | var preferences = EasyTipView.globalPreferences
88 | preferences.drawing.shadowColor = UIColor.black
89 | preferences.drawing.shadowRadius = 2
90 | preferences.drawing.shadowOpacity = 0.75
91 |
92 | let tip = EasyTipView(text: text, preferences: preferences, delegate: self)
93 | tip.show(forItem: toolbarItem)
94 | tipView = tip
95 | }
96 | }
97 |
98 | @IBAction func buttonAction(sender : UIButton) {
99 | switch sender {
100 | case buttonA:
101 |
102 | var preferences = EasyTipView.Preferences()
103 | preferences.drawing.backgroundColor = UIColor(hue:0.58, saturation:0.1, brightness:1, alpha:1)
104 | preferences.drawing.foregroundColor = UIColor.darkGray
105 | preferences.drawing.textAlignment = NSTextAlignment.center
106 |
107 | preferences.animating.dismissTransform = CGAffineTransform(translationX: 100, y: 0)
108 | preferences.animating.showInitialTransform = CGAffineTransform(translationX: -100, y: 0)
109 | preferences.animating.showInitialAlpha = 0
110 | preferences.animating.showDuration = 1
111 | preferences.animating.dismissDuration = 1
112 |
113 | let view = EasyTipView(text: "Tip view within the green superview. Tap to dismiss.", preferences: preferences)
114 | view.show(forView: buttonA, withinSuperview: self.smallContainerView)
115 |
116 | case buttonB:
117 |
118 | var preferences = EasyTipView.globalPreferences
119 | preferences.drawing.foregroundColor = UIColor.white
120 | preferences.drawing.font = UIFont(name: "HelveticaNeue-Light", size: 14)!
121 | preferences.drawing.textAlignment = NSTextAlignment.justified
122 |
123 | preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: -15)
124 | preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: 15)
125 | preferences.animating.showInitialAlpha = 0
126 | preferences.animating.showDuration = 1
127 | preferences.animating.dismissDuration = 1
128 | preferences.drawing.arrowPosition = .top
129 |
130 | let text = "Tip view inside the navigation controller's view. Tap to dismiss!"
131 | EasyTipView.show(forView: self.buttonB,
132 | withinSuperview: self.navigationController?.view,
133 | text: text,
134 | preferences: preferences)
135 |
136 | case buttonC:
137 |
138 | var preferences = EasyTipView.globalPreferences
139 | preferences.drawing.backgroundColor = buttonC.backgroundColor!
140 |
141 | preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: -15)
142 | preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: -15)
143 | preferences.animating.showInitialAlpha = 0
144 | preferences.animating.showDuration = 1.5
145 | preferences.animating.dismissDuration = 1.5
146 | preferences.drawing.arrowPosition = .top
147 |
148 | let text = "This tip view cannot be presented with the arrow on the top position, so position bottom has been chosen instead. Tap to dismiss."
149 | EasyTipView.show(forView: buttonC,
150 | withinSuperview: navigationController?.view,
151 | text: text,
152 | preferences: preferences)
153 |
154 | case buttonE:
155 |
156 | var preferences = EasyTipView.Preferences()
157 | preferences.drawing.backgroundColor = buttonE.backgroundColor!
158 | preferences.drawing.foregroundColor = UIColor.white
159 | preferences.drawing.textAlignment = NSTextAlignment.center
160 |
161 | preferences.drawing.arrowPosition = .right
162 |
163 | preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: 100)
164 | preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: -100)
165 | preferences.animating.showInitialAlpha = 0
166 | preferences.animating.showDuration = 1
167 | preferences.animating.dismissDuration = 1
168 |
169 | preferences.positioning.maxWidth = 150
170 |
171 | let view = EasyTipView(text: "Tip view positioned with the arrow on the right. Tap to dismiss.", preferences: preferences)
172 | view.show(forView: buttonE, withinSuperview: self.navigationController?.view!)
173 |
174 | case buttonF:
175 |
176 | var preferences = EasyTipView.Preferences()
177 | preferences.drawing.backgroundColor = buttonF.backgroundColor!
178 | preferences.drawing.foregroundColor = UIColor.white
179 | preferences.drawing.textAlignment = NSTextAlignment.center
180 |
181 | preferences.drawing.arrowPosition = .left
182 |
183 | preferences.animating.dismissTransform = CGAffineTransform(translationX: -30, y: -100)
184 | preferences.animating.showInitialTransform = CGAffineTransform(translationX: 30, y: 100)
185 | preferences.animating.showInitialAlpha = 0
186 | preferences.animating.showDuration = 1
187 | preferences.animating.dismissDuration = 1
188 | preferences.animating.dismissOnTap = false
189 |
190 | preferences.positioning.maxWidth = 150
191 |
192 | let view = EasyTipView(text: "Tip view positioned with the arrow on the left. Tap won't dismiss.", preferences: preferences)
193 | view.show(forView: buttonF, withinSuperview: self.navigationController?.view!)
194 |
195 | case buttonG:
196 |
197 | var preferences = EasyTipView.globalPreferences
198 | preferences.drawing.backgroundColor = buttonG.backgroundColor!
199 |
200 | preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: -15)
201 | preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: 15)
202 | preferences.animating.showInitialAlpha = 0
203 | preferences.animating.showDuration = 1
204 | preferences.animating.dismissDuration = 1
205 | preferences.drawing.arrowPosition = .bottom
206 |
207 | preferences.positioning.contentInsets = UIEdgeInsetsMake(5, 5, 5, 5)
208 |
209 | let contentView = UIImageView(frame: CGRect(x: 0, y: 0, width: 300, height: 82))
210 | contentView.image = UIImage(named: "easytipview")
211 | EasyTipView.show(forView: self.buttonG,
212 | contentView: contentView,
213 | preferences: preferences)
214 |
215 | default:
216 |
217 | var preferences = EasyTipView.globalPreferences
218 | preferences.drawing.arrowPosition = .bottom
219 | preferences.drawing.font = UIFont.systemFont(ofSize: 14)
220 | preferences.drawing.textAlignment = .center
221 | preferences.drawing.backgroundColor = buttonD.backgroundColor!
222 |
223 | preferences.positioning.maxWidth = 230
224 |
225 | preferences.animating.dismissTransform = CGAffineTransform(translationX: 100, y: 0)
226 | preferences.animating.showInitialTransform = CGAffineTransform(translationX: 100, y: 0)
227 | preferences.animating.showInitialAlpha = 0
228 | preferences.animating.showDuration = 1
229 | preferences.animating.dismissDuration = 1
230 |
231 | let firstAttributes: [NSAttributedString.Key: Any] = [.backgroundColor: UIColor(hue:0.07, saturation:0.82, brightness:0.89, alpha:1.00), .font: UIFont.systemFont(ofSize: 14)]
232 | let secondAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white, .backgroundColor: UIColor(hue:0.79, saturation:0.67, brightness:0.63, alpha:1.00), .font: UIFont.systemFont(ofSize: 14)]
233 | let thirdAttributes: [NSAttributedString.Key: Any] = [.backgroundColor: UIColor.white, .font: UIFont.systemFont(ofSize: 14)]
234 | let fourthAttributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white, .backgroundColor: UIColor(hue:0.59, saturation:0.44, brightness:0.27, alpha:1.00), .font: UIFont.systemFont(ofSize: 14)]
235 |
236 | let firstString = NSMutableAttributedString(string: "Tooltip in ", attributes: firstAttributes)
237 | let secondString = NSAttributedString(string: "top most ", attributes: secondAttributes)
238 | let thirdString = NSAttributedString(string: " window.\n", attributes: thirdAttributes)
239 | let fourthString = NSAttributedString(string: "Tap to dismiss.", attributes: fourthAttributes)
240 |
241 | firstString.append(secondString)
242 | firstString.append(thirdString)
243 | firstString.append(fourthString)
244 |
245 | EasyTipView.show(forView: self.buttonD,
246 | attributedText: firstString,
247 | preferences: preferences)
248 | }
249 | }
250 |
251 | func configureUI () {
252 | let color = UIColor(hue:0.46, saturation:0.99, brightness:0.6, alpha:1)
253 |
254 | buttonA.backgroundColor = UIColor(hue:0.58, saturation:0.1, brightness:1, alpha:1)
255 |
256 | self.navigationController?.view.tintColor = color
257 |
258 | self.buttonB.backgroundColor = color
259 | self.smallContainerView.backgroundColor = color
260 | }
261 | }
262 |
263 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Teodor Patraş
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "EasyTipView",
8 | platforms: [
9 | .iOS(.v9),
10 | ],
11 | products: [
12 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
13 | .library(
14 | name: "EasyTipView",
15 | targets: ["EasyTipView"]),
16 | ],
17 | dependencies: [
18 | // Dependencies declare other packages that this package depends on.
19 | // .package(url: /* package url */, from: "1.0.0"),
20 | ],
21 | targets: [
22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
23 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
24 | .target(
25 | name: "EasyTipView",
26 | dependencies: [],
27 | exclude: ["assets", "Example", "Source"]),
28 | .testTarget(
29 | name: "EasyTipViewTests",
30 | dependencies: ["EasyTipView"],
31 | exclude: ["assets", "Example", "Source"]),
32 | ]
33 | )
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 | [](http://cocoapods.org/pods/EasyTipView)
5 | [](https://travis-ci.org/teodorpatras/EasyTipView)
6 | [](http://cocoapods.org/pods/EasyTipView)
7 | [](https://github.com/Carthage/Carthage)
8 | [](http://cocoapods.org/pods/EasyTipView)
9 |
10 | Description
11 | --------------
12 |
13 | ```EasyTipView``` is a fully customizable tooltip view written in Swift that can be used as a call to action or informative tip.
14 |
15 | |
|
|
16 | |----------|-------------|
17 |
18 | # Contents
19 | 1. [Features](#features)
20 | 3. [Installation](#installation)
21 | 4. [Supported OS & SDK versions](#supported-versions)
22 | 5. [Usage](#usage)
23 | 6. [Customizing the appearance](#customising)
24 | 7. [Customising the presentation and dismissal animations](#customising-animations)
25 | 8. [License](#license)
26 | 9. [Contact](#contact)
27 |
28 | Features
29 | --------------
30 |
31 | - [x] Can be shown pointing to any ``UIBarItem`` or ``UIView`` subclass.
32 | - [x] Support for any arrow direction `←, →, ↑, ↓`
33 | - [x] Automatic orientation change adjustments.
34 | - [x] Fully customizable appearance (custom content view or simply just text - including `NSAttributedString` - see the Example app).
35 | - [x] Fully customizable presentation and dismissal animations.
36 |
37 |
38 | Installation
39 | --------------
40 |
41 | ### CocoaPods
42 |
43 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects.
44 |
45 | CocoaPods 0.36 adds supports for Swift and embedded frameworks. You can install it with the following command:
46 |
47 | ```bash
48 | $ gem install cocoapods
49 | ```
50 |
51 | To integrate EasyTipView into your Xcode project using CocoaPods, specify it in your `Podfile`:
52 |
53 |
54 | ```ruby
55 | pod 'EasyTipView', '~> 2.1'
56 | ```
57 |
58 | Then, run the following command:
59 |
60 | ```bash
61 | $ pod install
62 | ```
63 |
64 | In case Xcode complains ("Cannot load underlying module for EasyTipView") go to Product and choose Clean (or simply press ⇧⌘K).
65 |
66 |
67 | ### Carthage
68 |
69 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
70 |
71 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
72 |
73 | ```bash
74 | $ brew update
75 | $ brew install carthage
76 | ```
77 |
78 | To integrate EasyTipView into your Xcode project using Carthage, specify it in your `Cartfile`:
79 |
80 | ```ogdl
81 | github "teodorpatras/EasyTipView"
82 | ```
83 |
84 | Run `carthage update` to build the framework and drag the built `EasyTipView.framework` into your Xcode project.
85 |
86 | ### Manually
87 |
88 | If you prefer not to use either of the aforementioned dependency managers, you can integrate EasyTipView into your project manually.
89 |
90 | Supported OS & SDK Versions
91 | -----------------------------
92 |
93 | * Supported build target - iOS 8+ (Xcode 8)
94 |
95 | Usage
96 | --------------
97 |
98 | 1) First you should customize the preferences:
99 | ```swift
100 |
101 | var preferences = EasyTipView.Preferences()
102 | preferences.drawing.font = UIFont(name: "Futura-Medium", size: 13)!
103 | preferences.drawing.foregroundColor = UIColor.whiteColor()
104 | preferences.drawing.backgroundColor = UIColor(hue:0.46, saturation:0.99, brightness:0.6, alpha:1)
105 | preferences.drawing.arrowPosition = EasyTipView.ArrowPosition.top
106 |
107 | /*
108 | * Optionally you can make these preferences global for all future EasyTipViews
109 | */
110 | EasyTipView.globalPreferences = preferences
111 |
112 | ```
113 |
114 | 2) Secondly call the ``show(animated: forView: withinSuperview: text: preferences: delegate:)`` method:
115 | ```swift
116 | EasyTipView.show(forView: self.buttonB,
117 | withinSuperview: self.navigationController?.view,
118 | text: "Tip view inside the navigation controller's view. Tap to dismiss!",
119 | preferences: preferences,
120 | delegate: self)
121 | ```
122 |
123 | **Note that if you set the** ```EasyTipView.globalPreferences```**, you can ommit the** ```preferences``` **parameter in all calls. Additionally, you can also ommit the** ``withinSuperview`` **parameter and the** ``EasyTipView`` **will be shown within the main application window**.
124 |
125 | *Alternatively, if you want to dismiss the ``EasyTipView`` programmatically later on, you can use one of the instance methods:*
126 |
127 | ```swift
128 |
129 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
130 | tipView.show(forView: someView, withinSuperview: someSuperview)
131 |
132 | // later on you can dismiss it
133 | tipView.dismiss()
134 | ```
135 | Customizing the appearance
136 | --------------
137 | In order to customize the `EasyTipView` appearance and behavior, you can play with the `Preferences` structure which encapsulates all the customizable properties of the ``EasyTipView``. These preferences have been split into three structures:
138 | * ```Drawing``` - encapsulates customizable properties specifying how ```EastTipView``` will be drawn on screen.
139 | * ```Positioning``` - encapsulates customizable properties specifying where ```EasyTipView``` will be drawn within its own bounds.
140 | * ```Animating``` - encapsulates customizable properties specifying how ```EasyTipView``` will animate on and off screen.
141 |
142 | | `Drawing ` attribute | Description |
143 | |----------|-------------|
144 | |`cornerRadius`| The corner radius of the tip view bubble.|
145 | |`arrowHeight`| The height of the arrow positioned at the top or bottom of the bubble.|
146 | |`arrowWidth`| The width of the above mentioned arrow.|
147 | |`foregroundColor`| The text color.|
148 | |`backgroundColor`| The background color of the bubble.|
149 | |`arrowPosition`| The position of the arrow. This can be:
**+** `.top`: on top of the bubble
**+** `.bottom`: at the bottom of the bubble.
**+** `.left`: on the left of the bubble
**+** `.right`: on the right of the bubble
**+** `.any`: use this option to let the `EasyTipView` automatically find the best arrow position.
**If the passed in arrow cannot be applied due to layout restrictions, a different arrow position will be automatically assigned.**|
150 | |`textAlignment`| The alignment of the text.|
151 | |`borderWidth`| Width of the optional border to be applied on the bubble.|
152 | |`borderColor`| Color of the optional border to be applied on the bubble. **In order for the border to be applied, `borderColor` needs to be different that `UIColor.clear` and `borderWidth` > 0**|
153 | |`font`| Font to be applied on the text. |
154 | |`shadowColor`| The color of the shadow (default `UIColor.clearcolor`).|
155 | |`shadowOpacity`| The opacity of the shadow (default `0`). **For the shadow to be drawn, both** `shadowColor` **and** `shadowOpacity` **must be set to a valid value.**|
156 | |`shadowRadius`| The radius of the shadow (default `0`).|
157 | |`shadowOffset` | The offset of the shadow. |
158 |
159 | | `Positioning ` attribute | Description |
160 | |----------|-------------|
161 | |`bubbleHInset`| Horizontal bubble inset within its container.|
162 | |`bubbleVInset`| Vertical bubble inset within its container.|
163 | |`contentHInset`| Content horizontal inset within the bubble.|
164 | |`contentVInset`| Content vertical inset within the bubble.|
165 | |`maxWidth`| Max bubble width.|
166 |
167 | | `Animating ` attribute | Description |
168 | |----------|-------------|
169 | |`dismissTransform`| `CGAffineTransform` specifying how the bubble will be dismissed. |
170 | |`showInitialTransform`| `CGAffineTransform` specifying the initial transform to be applied on the bubble before it is animated on screen. |
171 | |`showFinalTransform`| `CGAffineTransform` specifying how the bubble will be animated on screen. |
172 | |`springDamping`| Spring animation damping.|
173 | |`springVelocity`| Spring animation velocity.|
174 | |`showInitialAlpha`|Initial alpha to be applied on the tip view before it is animated on screen.|
175 | |`dismissFinalAlpha`|The alpha to be applied on the tip view when it is animating off screen.|
176 | |`showDuration`|Show animation duration.|
177 | |`dismissDuration`|Dismiss animation duration.|
178 | |`dismissOnTap`|Prevents view from dismissing on tap if it is set to false. (Default value is true.)|
179 |
180 | Customising the presentation or dismissal animations
181 | --------------
182 |
183 | The default animations for showing or dismissing are scale up and down. If you want to change the default behavior, you need to change the attributes of the ``animating`` property within the preferences. An example could be:
184 |
185 | ```swift
186 | preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: -15)
187 | preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: -15)
188 | preferences.animating.showInitialAlpha = 0
189 | preferences.animating.showDuration = 1.5
190 | preferences.animating.dismissDuration = 1.5
191 | ```
192 |
193 | This produces the following animations:
194 |
195 |
196 | For more animations, checkout the example project.
197 | *Once you configured the animations, a good idea would be to __make these preferences global__, for all future instances of `EasyTipView` by assigning it to ```EasyTipView.globalPreferences```.*
198 |
199 |
200 | License
201 | --------------
202 |
203 | ```EasyTipView``` is developed by [Teodor Patraş](https://www.teodorpatras.com) and is released under the MIT license. See the ```LICENSE``` file for details.
204 |
205 | Logo was created using Bud Icons Launch graphic by [Budi Tanrim](http://buditanrim.co) from [FlatIcon](http://www.flaticon.com/) which is licensed under [Creative Commons BY 3.0](http://creativecommons.org/licenses/by/3.0/). Made with [Logo Maker](http://logomakr.com).
206 |
207 | Contact
208 | --------------
209 |
210 | You can follow or drop me a line on [my Twitter account](https://twitter.com/teodorpatras). If you find any issues on the project, you can open a ticket. Pull requests are also welcome.
211 |
--------------------------------------------------------------------------------
/Sources/EasyTipView/EasyTipView.h:
--------------------------------------------------------------------------------
1 | //
2 | // EasyTipView.h
3 | // EasyTipView
4 | //
5 | // Created by Teodor Patras on 11/06/16.
6 | // Copyright © 2016 teodorpatras. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for EasyTipView.
12 | FOUNDATION_EXPORT double EasyTipViewVersionNumber;
13 |
14 | //! Project version string for EasyTipView.
15 | FOUNDATION_EXPORT const unsigned char EasyTipViewVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Sources/EasyTipView/EasyTipView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EasyTipView.swift
3 | //
4 | // Copyright (c) 2015 Teodor Patraş
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 |
24 | #if canImport(UIKit)
25 | import UIKit
26 |
27 | public protocol EasyTipViewDelegate : class {
28 | func easyTipViewDidTap(_ tipView: EasyTipView)
29 | func easyTipViewDidDismiss(_ tipView : EasyTipView)
30 | }
31 |
32 |
33 | // MARK: - Public methods extension
34 |
35 | public extension EasyTipView {
36 |
37 | // MARK:- Class methods -
38 |
39 | /**
40 | Presents an EasyTipView pointing to a particular UIBarItem instance within the specified superview
41 |
42 | - parameter animated: Pass true to animate the presentation.
43 | - parameter item: The UIBarButtonItem or UITabBarItem instance which the EasyTipView will be pointing to.
44 | - parameter superview: A view which is part of the UIBarButtonItem instances superview hierarchy. Ignore this parameter in order to display the EasyTipView within the main window.
45 | - parameter text: The text to be displayed.
46 | - parameter preferences: The preferences which will configure the EasyTipView.
47 | - parameter delegate: The delegate.
48 | */
49 | class func show(animated: Bool = true, forItem item: UIBarItem, withinSuperview superview: UIView? = nil, text: String, preferences: Preferences = EasyTipView.globalPreferences, delegate: EasyTipViewDelegate? = nil){
50 |
51 | if let view = item.view {
52 | show(animated: animated, forView: view, withinSuperview: superview, text: text, preferences: preferences, delegate: delegate)
53 | }
54 | }
55 |
56 | /**
57 | Presents an EasyTipView pointing to a particular UIView instance within the specified superview
58 |
59 | - parameter animated: Pass true to animate the presentation.
60 | - parameter view: The UIView instance which the EasyTipView will be pointing to.
61 | - parameter superview: A view which is part of the UIView instances superview hierarchy. Ignore this parameter in order to display the EasyTipView within the main window.
62 | - parameter text: The text to be displayed.
63 | - parameter preferences: The preferences which will configure the EasyTipView.
64 | - parameter delegate: The delegate.
65 | */
66 | class func show(animated: Bool = true, forView view: UIView, withinSuperview superview: UIView? = nil, text: String, preferences: Preferences = EasyTipView.globalPreferences, delegate: EasyTipViewDelegate? = nil){
67 |
68 | let ev = EasyTipView(text: text, preferences: preferences, delegate: delegate)
69 | ev.show(animated: animated, forView: view, withinSuperview: superview)
70 | }
71 |
72 | /**
73 | Presents an EasyTipView pointing to a particular UIBarItem instance within the specified superview
74 |
75 | - parameter animated: Pass true to animate the presentation.
76 | - parameter item: The UIBarButtonItem or UITabBarItem instance which the EasyTipView will be pointing to.
77 | - parameter superview: A view which is part of the UIBarButtonItem instances superview hierarchy. Ignore this parameter in order to display the EasyTipView within the main window.
78 | - parameter contentView: The view to be displayed.
79 | - parameter preferences: The preferences which will configure the EasyTipView.
80 | - parameter delegate: The delegate.
81 | */
82 | class func show(animated: Bool = true, forItem item: UIBarItem, withinSuperview superview: UIView? = nil, contentView: UIView, preferences: Preferences = EasyTipView.globalPreferences, delegate: EasyTipViewDelegate? = nil){
83 |
84 | if let view = item.view {
85 | show(animated: animated, forView: view, withinSuperview: superview, contentView: contentView, preferences: preferences, delegate: delegate)
86 | }
87 | }
88 |
89 | /**
90 | Presents an EasyTipView pointing to a particular UIView instance within the specified superview
91 |
92 | - parameter animated: Pass true to animate the presentation.
93 | - parameter view: The UIView instance which the EasyTipView will be pointing to.
94 | - parameter superview: A view which is part of the UIView instances superview hierarchy. Ignore this parameter in order to display the EasyTipView within the main window.
95 | - parameter contentView: The view to be displayed.
96 | - parameter preferences: The preferences which will configure the EasyTipView.
97 | - parameter delegate: The delegate.
98 | */
99 | class func show(animated: Bool = true, forView view: UIView, withinSuperview superview: UIView? = nil, contentView: UIView, preferences: Preferences = EasyTipView.globalPreferences, delegate: EasyTipViewDelegate? = nil){
100 |
101 | let ev = EasyTipView(contentView: contentView, preferences: preferences, delegate: delegate)
102 | ev.show(animated: animated, forView: view, withinSuperview: superview)
103 | }
104 |
105 | /**
106 | Presents an EasyTipView pointing to a particular UIView instance within the specified superview containing attributed text.
107 | - parameter animated: Pass true to animate the presentation.
108 | - parameter view: The UIView instance which the EasyTipView will be pointing to.
109 | - parameter superview: A view which is part of the UIView instances superview hierarchy. Ignore this parameter in order to display the EasyTipView within the main window.
110 | - parameter attributedText: The attributed text to be displayed.
111 | - parameter preferences: The preferences which will configure the EasyTipView.
112 | - parameter delegate: The delegate.
113 | */
114 | class func show(animated: Bool = true, forView view: UIView, withinSuperview superview: UIView? = nil, attributedText: NSAttributedString, preferences: Preferences = EasyTipView.globalPreferences, delegate: EasyTipViewDelegate? = nil){
115 |
116 | let ev = EasyTipView(text: attributedText, preferences: preferences, delegate: delegate)
117 | ev.show(animated: animated, forView: view, withinSuperview: superview)
118 | }
119 |
120 | // MARK:- Instance methods -
121 |
122 | /**
123 | Presents an EasyTipView pointing to a particular UIBarItem instance within the specified superview
124 |
125 | - parameter animated: Pass true to animate the presentation.
126 | - parameter item: The UIBarButtonItem or UITabBarItem instance which the EasyTipView will be pointing to.
127 | - parameter superview: A view which is part of the UIBarButtonItem instances superview hierarchy. Ignore this parameter in order to display the EasyTipView within the main window.
128 | */
129 | func show(animated: Bool = true, forItem item: UIBarItem, withinSuperView superview: UIView? = nil) {
130 | if let view = item.view {
131 | show(animated: animated, forView: view, withinSuperview: superview)
132 | }
133 | }
134 |
135 | /**
136 | Presents an EasyTipView pointing to a particular UIView instance within the specified superview
137 |
138 | - parameter animated: Pass true to animate the presentation.
139 | - parameter view: The UIView instance which the EasyTipView will be pointing to.
140 | - parameter superview: A view which is part of the UIView instances superview hierarchy. Ignore this parameter in order to display the EasyTipView within the main window.
141 | */
142 | func show(animated: Bool = true, forView view: UIView, withinSuperview superview: UIView? = nil) {
143 |
144 | #if TARGET_APP_EXTENSIONS
145 | precondition(superview != nil, "The supplied superview parameter cannot be nil for app extensions.")
146 |
147 | let superview = superview!
148 | #else
149 | precondition(superview == nil || view.hasSuperview(superview!), "The supplied superview <\(superview!)> is not a direct nor an indirect superview of the supplied reference view <\(view)>. The superview passed to this method should be a direct or an indirect superview of the reference view. To display the tooltip within the main window, ignore the superview parameter.")
150 |
151 | let superview = superview ?? UIApplication.shared.windows.first!
152 | #endif
153 |
154 | let initialTransform = preferences.animating.showInitialTransform
155 | let finalTransform = preferences.animating.showFinalTransform
156 | let initialAlpha = preferences.animating.showInitialAlpha
157 | let damping = preferences.animating.springDamping
158 | let velocity = preferences.animating.springVelocity
159 |
160 | presentingView = view
161 | arrange(withinSuperview: superview)
162 |
163 | transform = initialTransform
164 | alpha = initialAlpha
165 |
166 | let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
167 | addGestureRecognizer(tap)
168 |
169 | superview.addSubview(self)
170 |
171 | let animations : () -> () = {
172 | self.transform = finalTransform
173 | self.alpha = 1
174 | }
175 |
176 | if animated {
177 | UIView.animate(withDuration: preferences.animating.showDuration, delay: 0, usingSpringWithDamping: damping, initialSpringVelocity: velocity, options: [.curveEaseInOut], animations: animations, completion: nil)
178 | }else{
179 | animations()
180 | }
181 | }
182 |
183 | /**
184 | Dismisses the EasyTipView
185 |
186 | - parameter completion: Completion block to be executed after the EasyTipView is dismissed.
187 | */
188 | func dismiss(withCompletion completion: (() -> ())? = nil){
189 |
190 | let damping = preferences.animating.springDamping
191 | let velocity = preferences.animating.springVelocity
192 |
193 | UIView.animate(withDuration: preferences.animating.dismissDuration, delay: 0, usingSpringWithDamping: damping, initialSpringVelocity: velocity, options: [.curveEaseInOut], animations: {
194 | self.transform = self.preferences.animating.dismissTransform
195 | self.alpha = self.preferences.animating.dismissFinalAlpha
196 | }) { (finished) -> Void in
197 | completion?()
198 | self.delegate?.easyTipViewDidDismiss(self)
199 | self.removeFromSuperview()
200 | self.transform = CGAffineTransform.identity
201 | }
202 | }
203 | }
204 |
205 | // MARK: - EasyTipView class implementation -
206 |
207 | open class EasyTipView: UIView {
208 |
209 | // MARK:- Nested types -
210 |
211 | public enum ArrowPosition {
212 | case any
213 | case top
214 | case bottom
215 | case right
216 | case left
217 |
218 | static let allValues = [top, bottom, right, left]
219 | }
220 |
221 | public struct Preferences {
222 |
223 | public struct Drawing {
224 | public var cornerRadius = CGFloat(5)
225 | public var arrowHeight = CGFloat(5)
226 | public var arrowWidth = CGFloat(10)
227 | public var foregroundColor = UIColor.white
228 | public var backgroundColor = UIColor.red
229 | public var arrowPosition = ArrowPosition.any
230 | public var textAlignment = NSTextAlignment.center
231 | public var borderWidth = CGFloat(0)
232 | public var borderColor = UIColor.clear
233 | public var font = UIFont.systemFont(ofSize: 15)
234 | public var shadowColor = UIColor.clear
235 | public var shadowOffset = CGSize(width: 0.0, height: 0.0)
236 | public var shadowRadius = CGFloat(0)
237 | public var shadowOpacity = CGFloat(0)
238 | }
239 |
240 | public struct Positioning {
241 | public var bubbleInsets = UIEdgeInsets(top: 1.0, left: 1.0, bottom: 1.0, right: 1.0)
242 | public var contentInsets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
243 | public var maxWidth = CGFloat(200)
244 | }
245 |
246 | public struct Animating {
247 | public var dismissTransform = CGAffineTransform(scaleX: 0.1, y: 0.1)
248 | public var showInitialTransform = CGAffineTransform(scaleX: 0, y: 0)
249 | public var showFinalTransform = CGAffineTransform.identity
250 | public var springDamping = CGFloat(0.7)
251 | public var springVelocity = CGFloat(0.7)
252 | public var showInitialAlpha = CGFloat(0)
253 | public var dismissFinalAlpha = CGFloat(0)
254 | public var showDuration = 0.7
255 | public var dismissDuration = 0.7
256 | public var dismissOnTap = true
257 | }
258 |
259 | public var drawing = Drawing()
260 | public var positioning = Positioning()
261 | public var animating = Animating()
262 | public var hasBorder : Bool {
263 | return drawing.borderWidth > 0 && drawing.borderColor != UIColor.clear
264 | }
265 |
266 | public var hasShadow : Bool {
267 | return drawing.shadowOpacity > 0 && drawing.shadowColor != UIColor.clear
268 | }
269 |
270 | public init() {}
271 | }
272 |
273 | public enum Content: CustomStringConvertible {
274 |
275 | case text(String)
276 | case attributedText(NSAttributedString)
277 | case view(UIView)
278 |
279 | public var description: String {
280 | switch self {
281 | case .text(let text):
282 | return "text : '\(text)'"
283 | case .attributedText(let text):
284 | return "attributed text : '\(text)'"
285 | case .view(let contentView):
286 | return "view : \(contentView)"
287 | }
288 | }
289 | }
290 |
291 |
292 | // MARK:- Variables -
293 |
294 | override open var backgroundColor: UIColor? {
295 | didSet {
296 | guard let color = backgroundColor
297 | , color != UIColor.clear else {return}
298 |
299 | preferences.drawing.backgroundColor = color
300 | backgroundColor = UIColor.clear
301 | }
302 | }
303 |
304 | override open var description: String {
305 | let type = "'\(String(reflecting: Swift.type(of: self)))'".components(separatedBy: ".").last!
306 |
307 | return "<< \(type) with \(content) >>"
308 | }
309 |
310 | fileprivate weak var presentingView: UIView?
311 | fileprivate weak var delegate: EasyTipViewDelegate?
312 | fileprivate var arrowTip = CGPoint.zero
313 | fileprivate(set) open var preferences: Preferences
314 | private let content: Content
315 |
316 | // MARK: - Lazy variables -
317 |
318 | fileprivate lazy var contentSize: CGSize = {
319 |
320 | [unowned self] in
321 |
322 | switch content {
323 | case .text(let text):
324 | #if swift(>=4.2)
325 | var attributes = [NSAttributedString.Key.font : self.preferences.drawing.font]
326 | #else
327 | var attributes = [NSAttributedStringKey.font : self.preferences.drawing.font]
328 | #endif
329 |
330 | var textSize = text.boundingRect(with: CGSize(width: self.preferences.positioning.maxWidth, height: CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attributes, context: nil).size
331 |
332 | textSize.width = ceil(textSize.width)
333 | textSize.height = ceil(textSize.height)
334 |
335 | if textSize.width < self.preferences.drawing.arrowWidth {
336 | textSize.width = self.preferences.drawing.arrowWidth
337 | }
338 |
339 | return textSize
340 |
341 | case .attributedText(let text):
342 | var textSize = text.boundingRect(with: CGSize(width: self.preferences.positioning.maxWidth, height: CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil).size
343 |
344 | textSize.width = ceil(textSize.width)
345 | textSize.height = ceil(textSize.height)
346 |
347 | if textSize.width < self.preferences.drawing.arrowWidth {
348 | textSize.width = self.preferences.drawing.arrowWidth
349 | }
350 |
351 | return textSize
352 |
353 | case .view(let contentView):
354 | return contentView.frame.size
355 | }
356 | }()
357 |
358 | fileprivate lazy var tipViewSize: CGSize = {
359 |
360 | [unowned self] in
361 |
362 | var tipViewSize =
363 | CGSize(
364 | width: self.contentSize.width + self.preferences.positioning.contentInsets.left + self.preferences.positioning.contentInsets.right + self.preferences.positioning.bubbleInsets.left + self.preferences.positioning.bubbleInsets.right,
365 | height: self.contentSize.height + self.preferences.positioning.contentInsets.top + self.preferences.positioning.contentInsets.bottom + self.preferences.positioning.bubbleInsets.top + self.preferences.positioning.bubbleInsets.bottom + self.preferences.drawing.arrowHeight)
366 |
367 | return tipViewSize
368 | }()
369 |
370 | // MARK: - Static variables -
371 |
372 | public static var globalPreferences = Preferences()
373 |
374 | // MARK:- Initializer -
375 |
376 | public convenience init (text: String, preferences: Preferences = EasyTipView.globalPreferences, delegate: EasyTipViewDelegate? = nil) {
377 | self.init(content: .text(text), preferences: preferences, delegate: delegate)
378 |
379 | self.isAccessibilityElement = true
380 | self.accessibilityTraits = UIAccessibilityTraits.staticText
381 | self.accessibilityLabel = text
382 | }
383 |
384 | public convenience init (contentView: UIView, preferences: Preferences = EasyTipView.globalPreferences, delegate: EasyTipViewDelegate? = nil) {
385 | self.init(content: .view(contentView), preferences: preferences, delegate: delegate)
386 | }
387 |
388 | public convenience init (text: NSAttributedString, preferences: Preferences = EasyTipView.globalPreferences, delegate: EasyTipViewDelegate? = nil) {
389 | self.init(content: .attributedText(text), preferences: preferences, delegate: delegate)
390 | }
391 |
392 | public init (content: Content, preferences: Preferences = EasyTipView.globalPreferences, delegate: EasyTipViewDelegate? = nil) {
393 |
394 | self.content = content
395 | self.preferences = preferences
396 | self.delegate = delegate
397 |
398 | super.init(frame: CGRect.zero)
399 |
400 | self.backgroundColor = UIColor.clear
401 |
402 | #if swift(>=4.2)
403 | let notificationName = UIDevice.orientationDidChangeNotification
404 | #else
405 | let notificationName = NSNotification.Name.UIDeviceOrientationDidChange
406 | #endif
407 |
408 | NotificationCenter.default.addObserver(self, selector: #selector(handleRotation), name: notificationName, object: nil)
409 | }
410 |
411 | deinit
412 | {
413 | NotificationCenter.default.removeObserver(self)
414 | }
415 |
416 | /**
417 | NSCoding not supported. Use init(text, preferences, delegate) instead!
418 | */
419 | required public init?(coder aDecoder: NSCoder) {
420 | fatalError("NSCoding not supported. Use init(text, preferences, delegate) instead!")
421 | }
422 |
423 | // MARK: - Rotation support -
424 |
425 | @objc func handleRotation() {
426 | guard let sview = superview
427 | , presentingView != nil else { return }
428 |
429 | UIView.animate(withDuration: 0.3) {
430 | self.arrange(withinSuperview: sview)
431 | self.setNeedsDisplay()
432 | }
433 | }
434 |
435 | // MARK: - Private methods -
436 |
437 | fileprivate func computeFrame(arrowPosition position: ArrowPosition, refViewFrame: CGRect, superviewFrame: CGRect) -> CGRect {
438 | var xOrigin: CGFloat = 0
439 | var yOrigin: CGFloat = 0
440 |
441 | switch position {
442 | case .top, .any:
443 | xOrigin = refViewFrame.center.x - tipViewSize.width / 2
444 | yOrigin = refViewFrame.y + refViewFrame.height
445 | case .bottom:
446 | xOrigin = refViewFrame.center.x - tipViewSize.width / 2
447 | yOrigin = refViewFrame.y - tipViewSize.height
448 | case .right:
449 | xOrigin = refViewFrame.x - tipViewSize.width
450 | yOrigin = refViewFrame.center.y - tipViewSize.height / 2
451 | case .left:
452 | xOrigin = refViewFrame.x + refViewFrame.width
453 | yOrigin = refViewFrame.center.y - tipViewSize.height / 2
454 | }
455 |
456 | var frame = CGRect(x: xOrigin, y: yOrigin, width: tipViewSize.width, height: tipViewSize.height)
457 | adjustFrame(&frame, forSuperviewFrame: superviewFrame)
458 | return frame
459 | }
460 |
461 | fileprivate func adjustFrame(_ frame: inout CGRect, forSuperviewFrame superviewFrame: CGRect) {
462 |
463 | // adjust horizontally
464 | if frame.x < 0 {
465 | frame.x = 0
466 | } else if frame.maxX > superviewFrame.width {
467 | frame.x = superviewFrame.width - frame.width
468 | }
469 |
470 | //adjust vertically
471 | if frame.y < 0 {
472 | frame.y = 0
473 | } else if frame.maxY > superviewFrame.maxY {
474 | frame.y = superviewFrame.height - frame.height
475 | }
476 | }
477 |
478 | fileprivate func isFrameValid(_ frame: CGRect, forRefViewFrame: CGRect, withinSuperviewFrame: CGRect) -> Bool {
479 | return !frame.intersects(forRefViewFrame)
480 | }
481 |
482 | fileprivate func arrange(withinSuperview superview: UIView) {
483 |
484 | var position = preferences.drawing.arrowPosition
485 |
486 | let refViewFrame = presentingView!.convert(presentingView!.bounds, to: superview);
487 |
488 | let superviewFrame: CGRect
489 | if let scrollview = superview as? UIScrollView {
490 | superviewFrame = CGRect(origin: scrollview.frame.origin, size: scrollview.contentSize)
491 | } else {
492 | superviewFrame = superview.frame
493 | }
494 |
495 | var frame = computeFrame(arrowPosition: position, refViewFrame: refViewFrame, superviewFrame: superviewFrame)
496 |
497 | if !isFrameValid(frame, forRefViewFrame: refViewFrame, withinSuperviewFrame: superviewFrame) {
498 | for value in ArrowPosition.allValues where value != position {
499 | let newFrame = computeFrame(arrowPosition: value, refViewFrame: refViewFrame, superviewFrame: superviewFrame)
500 | if isFrameValid(newFrame, forRefViewFrame: refViewFrame, withinSuperviewFrame: superviewFrame) {
501 |
502 | if position != .any {
503 | print("[EasyTipView - Info] The arrow position you chose <\(position)> could not be applied. Instead, position <\(value)> has been applied! Please specify position <\(ArrowPosition.any)> if you want EasyTipView to choose a position for you.")
504 | }
505 |
506 | frame = newFrame
507 | position = value
508 | preferences.drawing.arrowPosition = value
509 | break
510 | }
511 | }
512 | }
513 |
514 | switch position {
515 | case .bottom, .top, .any:
516 | var arrowTipXOrigin: CGFloat
517 | if frame.width < refViewFrame.width {
518 | arrowTipXOrigin = tipViewSize.width / 2
519 | } else {
520 | arrowTipXOrigin = abs(frame.x - refViewFrame.x) + refViewFrame.width / 2
521 | }
522 |
523 | arrowTip = CGPoint(x: arrowTipXOrigin, y: position == .bottom ? tipViewSize.height - preferences.positioning.bubbleInsets.bottom : preferences.positioning.bubbleInsets.top)
524 | case .right, .left:
525 | var arrowTipYOrigin: CGFloat
526 | if frame.height < refViewFrame.height {
527 | arrowTipYOrigin = tipViewSize.height / 2
528 | } else {
529 | arrowTipYOrigin = abs(frame.y - refViewFrame.y) + refViewFrame.height / 2
530 | }
531 |
532 | arrowTip = CGPoint(x: preferences.drawing.arrowPosition == .left ? preferences.positioning.bubbleInsets.left : tipViewSize.width - preferences.positioning.bubbleInsets.right, y: arrowTipYOrigin)
533 | }
534 |
535 | if case .view(let contentView) = content {
536 | contentView.translatesAutoresizingMaskIntoConstraints = false
537 | contentView.frame = getContentRect(from: getBubbleFrame())
538 | }
539 |
540 | self.frame = frame
541 | }
542 |
543 | // MARK:- Callbacks -
544 |
545 | @objc func handleTap() {
546 | self.delegate?.easyTipViewDidTap(self)
547 | guard preferences.animating.dismissOnTap else { return }
548 | dismiss()
549 | }
550 |
551 | // MARK:- Drawing -
552 |
553 | fileprivate func drawBubble(_ bubbleFrame: CGRect, arrowPosition: ArrowPosition, context: CGContext) {
554 |
555 | let arrowWidth = preferences.drawing.arrowWidth
556 | let arrowHeight = preferences.drawing.arrowHeight
557 | let cornerRadius = preferences.drawing.cornerRadius
558 |
559 | let contourPath = CGMutablePath()
560 |
561 | contourPath.move(to: CGPoint(x: arrowTip.x, y: arrowTip.y))
562 |
563 | switch arrowPosition {
564 | case .bottom, .top, .any:
565 |
566 | contourPath.addLine(to: CGPoint(x: arrowTip.x - arrowWidth / 2, y: arrowTip.y + (arrowPosition == .bottom ? -1 : 1) * arrowHeight))
567 | if arrowPosition == .bottom {
568 | drawBubbleBottomShape(bubbleFrame, cornerRadius: cornerRadius, path: contourPath)
569 | } else {
570 | drawBubbleTopShape(bubbleFrame, cornerRadius: cornerRadius, path: contourPath)
571 | }
572 | contourPath.addLine(to: CGPoint(x: arrowTip.x + arrowWidth / 2, y: arrowTip.y + (arrowPosition == .bottom ? -1 : 1) * arrowHeight))
573 |
574 | case .right, .left:
575 |
576 | contourPath.addLine(to: CGPoint(x: arrowTip.x + (arrowPosition == .right ? -1 : 1) * arrowHeight, y: arrowTip.y - arrowWidth / 2))
577 |
578 | if arrowPosition == .right {
579 | drawBubbleRightShape(bubbleFrame, cornerRadius: cornerRadius, path: contourPath)
580 | } else {
581 | drawBubbleLeftShape(bubbleFrame, cornerRadius: cornerRadius, path: contourPath)
582 | }
583 |
584 | contourPath.addLine(to: CGPoint(x: arrowTip.x + (arrowPosition == .right ? -1 : 1) * arrowHeight, y: arrowTip.y + arrowWidth / 2))
585 | }
586 |
587 | contourPath.closeSubpath()
588 | context.addPath(contourPath)
589 | context.clip()
590 |
591 | paintBubble(context)
592 |
593 | if preferences.hasBorder {
594 | drawBorder(contourPath, context: context)
595 | }
596 | }
597 |
598 | fileprivate func drawBubbleBottomShape(_ frame: CGRect, cornerRadius: CGFloat, path: CGMutablePath) {
599 |
600 | path.addArc(tangent1End: CGPoint(x: frame.x, y: frame.y + frame.height), tangent2End: CGPoint(x: frame.x, y: frame.y), radius: cornerRadius)
601 | path.addArc(tangent1End: CGPoint(x: frame.x, y: frame.y), tangent2End: CGPoint(x: frame.x + frame.width, y: frame.y), radius: cornerRadius)
602 | path.addArc(tangent1End: CGPoint(x: frame.x + frame.width, y: frame.y), tangent2End: CGPoint(x: frame.x + frame.width, y: frame.y + frame.height), radius: cornerRadius)
603 | path.addArc(tangent1End: CGPoint(x: frame.x + frame.width, y: frame.y + frame.height), tangent2End: CGPoint(x: frame.x, y: frame.y + frame.height), radius: cornerRadius)
604 | }
605 |
606 | fileprivate func drawBubbleTopShape(_ frame: CGRect, cornerRadius: CGFloat, path: CGMutablePath) {
607 |
608 | path.addArc(tangent1End: CGPoint(x: frame.x, y: frame.y), tangent2End: CGPoint(x: frame.x, y: frame.y + frame.height), radius: cornerRadius)
609 | path.addArc(tangent1End: CGPoint(x: frame.x, y: frame.y + frame.height), tangent2End: CGPoint(x: frame.x + frame.width, y: frame.y + frame.height), radius: cornerRadius)
610 | path.addArc(tangent1End: CGPoint(x: frame.x + frame.width, y: frame.y + frame.height), tangent2End: CGPoint(x: frame.x + frame.width, y: frame.y), radius: cornerRadius)
611 | path.addArc(tangent1End: CGPoint(x: frame.x + frame.width, y: frame.y), tangent2End: CGPoint(x: frame.x, y: frame.y), radius: cornerRadius)
612 | }
613 |
614 | fileprivate func drawBubbleRightShape(_ frame: CGRect, cornerRadius: CGFloat, path: CGMutablePath) {
615 |
616 | path.addArc(tangent1End: CGPoint(x: frame.x + frame.width, y: frame.y), tangent2End: CGPoint(x: frame.x, y: frame.y), radius: cornerRadius)
617 | path.addArc(tangent1End: CGPoint(x: frame.x, y: frame.y), tangent2End: CGPoint(x: frame.x, y: frame.y + frame.height), radius: cornerRadius)
618 | path.addArc(tangent1End: CGPoint(x: frame.x, y: frame.y + frame.height), tangent2End: CGPoint(x: frame.x + frame.width, y: frame.y + frame.height), radius: cornerRadius)
619 | path.addArc(tangent1End: CGPoint(x: frame.x + frame.width, y: frame.y + frame.height), tangent2End: CGPoint(x: frame.x + frame.width, y: frame.height), radius: cornerRadius)
620 |
621 | }
622 |
623 | fileprivate func drawBubbleLeftShape(_ frame: CGRect, cornerRadius: CGFloat, path: CGMutablePath) {
624 |
625 | path.addArc(tangent1End: CGPoint(x: frame.x, y: frame.y), tangent2End: CGPoint(x: frame.x + frame.width, y: frame.y), radius: cornerRadius)
626 | path.addArc(tangent1End: CGPoint(x: frame.x + frame.width, y: frame.y), tangent2End: CGPoint(x: frame.x + frame.width, y: frame.y + frame.height), radius: cornerRadius)
627 | path.addArc(tangent1End: CGPoint(x: frame.x + frame.width, y: frame.y + frame.height), tangent2End: CGPoint(x: frame.x, y: frame.y + frame.height), radius: cornerRadius)
628 | path.addArc(tangent1End: CGPoint(x: frame.x, y: frame.y + frame.height), tangent2End: CGPoint(x: frame.x, y: frame.y), radius: cornerRadius)
629 | }
630 |
631 | fileprivate func paintBubble(_ context: CGContext) {
632 | context.setFillColor(preferences.drawing.backgroundColor.cgColor)
633 | context.fill(bounds)
634 | }
635 |
636 | fileprivate func drawBorder(_ borderPath: CGPath, context: CGContext) {
637 | context.addPath(borderPath)
638 | context.setStrokeColor(preferences.drawing.borderColor.cgColor)
639 | context.setLineWidth(preferences.drawing.borderWidth)
640 | context.strokePath()
641 | }
642 |
643 | fileprivate func drawText(_ bubbleFrame: CGRect, context : CGContext) {
644 | guard case .text(let text) = content else { return }
645 | let paragraphStyle = NSMutableParagraphStyle()
646 | paragraphStyle.alignment = preferences.drawing.textAlignment
647 | paragraphStyle.lineBreakMode = NSLineBreakMode.byWordWrapping
648 |
649 |
650 | let textRect = getContentRect(from: bubbleFrame)
651 |
652 | #if swift(>=4.2)
653 | let attributes = [NSAttributedString.Key.font : preferences.drawing.font, NSAttributedString.Key.foregroundColor : preferences.drawing.foregroundColor, NSAttributedString.Key.paragraphStyle : paragraphStyle]
654 | #else
655 | let attributes = [NSAttributedStringKey.font : preferences.drawing.font, NSAttributedStringKey.foregroundColor : preferences.drawing.foregroundColor, NSAttributedStringKey.paragraphStyle : paragraphStyle]
656 | #endif
657 |
658 | text.draw(in: textRect, withAttributes: attributes)
659 | }
660 |
661 | fileprivate func drawAttributedText(_ bubbleFrame: CGRect, context : CGContext) {
662 | guard
663 | case .attributedText(let text) = content
664 | else {
665 | return
666 | }
667 |
668 | let textRect = getContentRect(from: bubbleFrame)
669 |
670 | text.draw(with: textRect, options: .usesLineFragmentOrigin, context: .none)
671 | }
672 |
673 | fileprivate func drawShadow() {
674 | if preferences.hasShadow {
675 | self.layer.masksToBounds = false
676 | self.layer.shadowColor = preferences.drawing.shadowColor.cgColor
677 | self.layer.shadowOffset = preferences.drawing.shadowOffset
678 | self.layer.shadowRadius = preferences.drawing.shadowRadius
679 | self.layer.shadowOpacity = Float(preferences.drawing.shadowOpacity)
680 | }
681 | }
682 |
683 | override open func draw(_ rect: CGRect) {
684 |
685 | let bubbleFrame = getBubbleFrame()
686 |
687 | let context = UIGraphicsGetCurrentContext()!
688 | context.saveGState ()
689 |
690 | drawBubble(bubbleFrame, arrowPosition: preferences.drawing.arrowPosition, context: context)
691 |
692 | switch content {
693 | case .text:
694 | drawText(bubbleFrame, context: context)
695 | case .attributedText:
696 | drawAttributedText(bubbleFrame, context: context)
697 | case .view (let view):
698 | addSubview(view)
699 | }
700 |
701 | drawShadow()
702 | context.restoreGState()
703 | }
704 |
705 | private func getBubbleFrame() -> CGRect {
706 | let arrowPosition = preferences.drawing.arrowPosition
707 | let bubbleWidth: CGFloat
708 | let bubbleHeight: CGFloat
709 | let bubbleXOrigin: CGFloat
710 | let bubbleYOrigin: CGFloat
711 | switch arrowPosition {
712 | case .bottom, .top, .any:
713 |
714 | bubbleWidth = tipViewSize.width - preferences.positioning.bubbleInsets.left - preferences.positioning.bubbleInsets.right
715 | bubbleHeight = tipViewSize.height - preferences.positioning.bubbleInsets.top - preferences.positioning.bubbleInsets.bottom - preferences.drawing.arrowHeight
716 |
717 | bubbleXOrigin = preferences.positioning.bubbleInsets.left
718 | bubbleYOrigin = arrowPosition == .bottom ? preferences.positioning.bubbleInsets.top : preferences.positioning.bubbleInsets.top + preferences.drawing.arrowHeight
719 |
720 | case .left, .right:
721 |
722 | bubbleWidth = tipViewSize.width - preferences.positioning.bubbleInsets.left - preferences.positioning.bubbleInsets.right - preferences.drawing.arrowHeight
723 | bubbleHeight = tipViewSize.height - preferences.positioning.bubbleInsets.top - preferences.positioning.bubbleInsets.left
724 |
725 | bubbleXOrigin = arrowPosition == .right ? preferences.positioning.bubbleInsets.left : preferences.positioning.bubbleInsets.left + preferences.drawing.arrowHeight
726 | bubbleYOrigin = preferences.positioning.bubbleInsets.top
727 |
728 | }
729 | return CGRect(x: bubbleXOrigin, y: bubbleYOrigin, width: bubbleWidth, height: bubbleHeight)
730 | }
731 |
732 | private func getContentRect(from bubbleFrame: CGRect) -> CGRect {
733 | return CGRect(x: bubbleFrame.origin.x + preferences.positioning.contentInsets.left, y: bubbleFrame.origin.y + preferences.positioning.contentInsets.top, width: contentSize.width, height: contentSize.height)
734 | }
735 | }
736 | #endif
737 |
--------------------------------------------------------------------------------
/Sources/EasyTipView/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Sources/EasyTipView/UIKitExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIKitExtensions.swift
3 | // EasyTipView
4 | //
5 | // Created by Teodor Patras on 29/06/16.
6 | // Copyright © 2016 teodorpatras. All rights reserved.
7 | //
8 |
9 | #if canImport(UIKit)
10 | import UIKit
11 |
12 | // MARK: - UIBarItem extension -
13 |
14 | extension UIBarItem {
15 | var view: UIView? {
16 | if let item = self as? UIBarButtonItem, let customView = item.customView {
17 | return customView
18 | }
19 | return self.value(forKey: "view") as? UIView
20 | }
21 | }
22 |
23 | // MARK:- UIView extension -
24 |
25 | extension UIView {
26 |
27 | func hasSuperview(_ superview: UIView) -> Bool{
28 | return viewHasSuperview(self, superview: superview)
29 | }
30 |
31 | fileprivate func viewHasSuperview(_ view: UIView, superview: UIView) -> Bool {
32 | if let sview = view.superview {
33 | if sview === superview {
34 | return true
35 | } else{
36 | return viewHasSuperview(sview, superview: superview)
37 | }
38 | } else{
39 | return false
40 | }
41 | }
42 | }
43 |
44 | // MARK:- CGRect extension -
45 |
46 | extension CGRect {
47 | var x: CGFloat {
48 | get {
49 | return self.origin.x
50 | }
51 | set {
52 | self.origin.x = newValue
53 | }
54 | }
55 |
56 | var y: CGFloat {
57 | get {
58 | return self.origin.y
59 | }
60 |
61 | set {
62 | self.origin.y = newValue
63 | }
64 | }
65 |
66 | var center: CGPoint {
67 | return CGPoint(x: self.x + self.width / 2, y: self.y + self.height / 2)
68 | }
69 | }
70 | #endif
71 |
--------------------------------------------------------------------------------
/Tests/EasyTipViewTests/Tests.swift:
--------------------------------------------------------------------------------
1 | #if canImport(UIKit)
2 | import UIKit
3 | import XCTest
4 | import EasyTipView
5 |
6 | class Tests: XCTestCase {
7 |
8 | var view : UIView!
9 | var superview : UIView!
10 |
11 | override func setUp() {
12 | super.setUp()
13 |
14 | superview = UIView(frame: CGRect(x: 0, y: 0, width: 600, height: 600))
15 | view = UIView(frame: CGRect(x: 0, y: 500, width: 100, height: 100))
16 |
17 | superview.addSubview(view)
18 | }
19 |
20 | func testViewPositionTop() {
21 | var preferences = EasyTipView.Preferences()
22 | preferences.drawing.arrowPosition = EasyTipView.ArrowPosition.top
23 |
24 | view.center = superview.center
25 |
26 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
27 | tipView.show(animated: false, forView: view, withinSuperview: superview)
28 |
29 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
30 | XCTAssert(tipView.preferences.drawing.arrowPosition == .top, "Position should be top")
31 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
32 | XCTAssert((tipView.frame).minY == (view.frame).maxY, "EasyTipView should be below the presenting view")
33 | }
34 |
35 | func testViewPositionBottom() {
36 |
37 | var preferences = EasyTipView.Preferences()
38 | preferences.drawing.arrowPosition = EasyTipView.ArrowPosition.bottom
39 |
40 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
41 | tipView.show(animated: false, forView: view, withinSuperview: superview)
42 |
43 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
44 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
45 | XCTAssert(tipView.preferences.drawing.arrowPosition == .bottom, "Position should be bottom")
46 | XCTAssert((tipView.frame).maxY == (view.frame).minY, "EasyTipView should be above the presenting view")
47 | }
48 |
49 | func testViewPositionLeft() {
50 |
51 | var preferences = EasyTipView.Preferences()
52 | preferences.drawing.arrowPosition = .left
53 |
54 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
55 | tipView.show(animated: false, forView: view, withinSuperview: superview)
56 |
57 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
58 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
59 | XCTAssert(tipView.preferences.drawing.arrowPosition == .left, "Position should be left")
60 | XCTAssert((tipView.frame).minX == (view.frame).maxX, "EasyTipView should be to the right of the presenting view")
61 | }
62 |
63 | func testViewPositionRight() {
64 |
65 | var preferences = EasyTipView.Preferences()
66 | preferences.drawing.arrowPosition = .right
67 |
68 | view.frame = CGRect(x: superview.frame.size.width - 100, y: 0, width: 100, height: 100)
69 |
70 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
71 | tipView.show(animated: false, forView: view, withinSuperview: superview)
72 |
73 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
74 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
75 | XCTAssert(tipView.preferences.drawing.arrowPosition == .right, "Position should be right")
76 | XCTAssert((tipView.frame).maxX == (view.frame).minX, "EasyTipView should be to the right of the presenting view")
77 | }
78 |
79 | func testViewPositionAny() {
80 |
81 | var preferences = EasyTipView.Preferences()
82 | preferences.drawing.arrowPosition = .any
83 |
84 | view.center = superview.center
85 |
86 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
87 | tipView.show(animated: false, forView: view, withinSuperview: superview)
88 |
89 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
90 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
91 | XCTAssert(tipView.preferences.drawing.arrowPosition == .any, "Position should be any")
92 | XCTAssert((tipView.frame).minY == (view.frame).maxY, "EasyTipView should be below the presenting view")
93 | }
94 |
95 | func testAutoPositionAdjustmentAny() {
96 | var preferences = EasyTipView.Preferences()
97 | preferences.drawing.arrowPosition = .any
98 |
99 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
100 | tipView.show(animated: false, forView: view, withinSuperview: superview)
101 |
102 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
103 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
104 | XCTAssert(tipView.preferences.drawing.arrowPosition == .bottom, "Position should be bottom")
105 | XCTAssert((tipView.frame).maxY == (view.frame).minY, "EasyTipView should be above the presenting view")
106 | }
107 |
108 | func testAutoPositionAdjustmentTop() {
109 | var preferences = EasyTipView.Preferences()
110 | preferences.drawing.arrowPosition = EasyTipView.ArrowPosition.top
111 |
112 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
113 | tipView.show(animated: false, forView: view, withinSuperview: superview)
114 |
115 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
116 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
117 | XCTAssert(tipView.preferences.drawing.arrowPosition == .bottom, "Position should be bottom")
118 | XCTAssert((tipView.frame).maxY == (view.frame).minY, "EasyTipView should be above the presenting view")
119 | }
120 |
121 | func testAutoPositionAdjustmentRight() {
122 | var preferences = EasyTipView.Preferences()
123 | preferences.drawing.arrowPosition = .right
124 |
125 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
126 | tipView.show(animated: false, forView: view, withinSuperview: superview)
127 |
128 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
129 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
130 | XCTAssert(tipView.preferences.drawing.arrowPosition == .bottom, "Position should be bottom")
131 | XCTAssert((tipView.frame).maxY == (view.frame).minY, "EasyTipView should be above the presenting view")
132 | }
133 |
134 | func testAutoPositionAdjustmentLeft() {
135 | var preferences = EasyTipView.Preferences()
136 | preferences.drawing.arrowPosition = .left
137 |
138 | view.frame = CGRect(x: superview.frame.size.width - 100, y: superview.frame.size.height - 100, width: 100, height: 100)
139 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
140 | tipView.show(animated: false, forView: view, withinSuperview: superview)
141 |
142 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
143 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
144 | XCTAssert(tipView.preferences.drawing.arrowPosition == .bottom, "Position should be bottom")
145 | XCTAssert((tipView.frame).maxY == (view.frame).minY, "EasyTipView should be above the presenting view")
146 | }
147 |
148 | func testAutoPositionAdjustmentBottom() {
149 |
150 | var frame = view.frame
151 | frame.origin.y = 0
152 |
153 | view.frame = frame
154 |
155 | var preferences = EasyTipView.Preferences()
156 | preferences.drawing.arrowPosition = EasyTipView.ArrowPosition.bottom
157 |
158 | let tipView = EasyTipView(text: "Some text", preferences: preferences)
159 | tipView.show(animated: false, forView: view, withinSuperview: superview)
160 |
161 | XCTAssert(tipView.superview === superview, "EasyTipView should be present")
162 | XCTAssert(tipView.alpha == 1, "EasyTipView should be visible")
163 | XCTAssert(tipView.preferences.drawing.arrowPosition == .top, "Position should be bottom")
164 | XCTAssert((tipView.frame).minY == (view.frame).maxY, "EasyTipView should be below the presenting view")
165 | }
166 | }
167 | #endif
168 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/assets/animation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/assets/animation.gif
--------------------------------------------------------------------------------
/assets/easytipview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/assets/easytipview.gif
--------------------------------------------------------------------------------
/assets/easytipview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/assets/easytipview.png
--------------------------------------------------------------------------------
/assets/static.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/teodorpatras/EasyTipView/8a9133085074c41119516a22b4223f79b8698b40/assets/static.png
--------------------------------------------------------------------------------