├── README.md
├── WXNearbyRadar.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── xinwu.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── xinwu.xcuserdatad
│ └── xcschemes
│ ├── WXNearbyRadar.xcscheme
│ └── xcschememanagement.plist
├── WXNearbyRadar
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ └── avatar.imageset
│ │ ├── Contents.json
│ │ └── avatar.png
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── GradientAngleCALayer.swift
├── Info.plist
├── RadarView.swift
└── ViewController.swift
└── demo.gif
/README.md:
--------------------------------------------------------------------------------
1 |
2 | WXNearbyRadar
3 | ====================
4 | This is a simple demo shows the Animation and UI of looking for nearby people.
5 |
6 |
7 |
8 | ## Requirements
9 | * Xcode 7 or higher
10 | * iOS 9 or higher
11 |
12 | ## Demo
13 | Build and run the `WXNearbyRadar` project in Xcode
14 |
15 | ## Contact
16 |
17 | - iridescent330@gmail.com
18 |
19 | ## License
20 |
21 | WXNearbyRadar is available under the MIT license.
22 |
23 | Copyright © 2016 Xin Wu.
24 |
25 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
26 |
27 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
28 |
--------------------------------------------------------------------------------
/WXNearbyRadar.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 683B28951C7D0E4600CD4FB6 /* GradientAngleCALayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 683B28941C7D0E4600CD4FB6 /* GradientAngleCALayer.swift */; };
11 | 6866B9871C7C04E000CDD62A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6866B9861C7C04E000CDD62A /* AppDelegate.swift */; };
12 | 6866B9891C7C04E000CDD62A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6866B9881C7C04E000CDD62A /* ViewController.swift */; };
13 | 6866B98C1C7C04E000CDD62A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6866B98A1C7C04E000CDD62A /* Main.storyboard */; };
14 | 6866B98E1C7C04E000CDD62A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6866B98D1C7C04E000CDD62A /* Assets.xcassets */; };
15 | 6866B9911C7C04E000CDD62A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6866B98F1C7C04E000CDD62A /* LaunchScreen.storyboard */; };
16 | 6866B9991C7C050800CDD62A /* RadarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6866B9981C7C050800CDD62A /* RadarView.swift */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | 683B28941C7D0E4600CD4FB6 /* GradientAngleCALayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientAngleCALayer.swift; sourceTree = ""; };
21 | 6866B9831C7C04E000CDD62A /* WXNearbyRadar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WXNearbyRadar.app; sourceTree = BUILT_PRODUCTS_DIR; };
22 | 6866B9861C7C04E000CDD62A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
23 | 6866B9881C7C04E000CDD62A /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
24 | 6866B98B1C7C04E000CDD62A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
25 | 6866B98D1C7C04E000CDD62A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
26 | 6866B9901C7C04E000CDD62A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
27 | 6866B9921C7C04E000CDD62A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
28 | 6866B9981C7C050800CDD62A /* RadarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadarView.swift; sourceTree = ""; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFrameworksBuildPhase section */
32 | 6866B9801C7C04E000CDD62A /* Frameworks */ = {
33 | isa = PBXFrameworksBuildPhase;
34 | buildActionMask = 2147483647;
35 | files = (
36 | );
37 | runOnlyForDeploymentPostprocessing = 0;
38 | };
39 | /* End PBXFrameworksBuildPhase section */
40 |
41 | /* Begin PBXGroup section */
42 | 6866B97A1C7C04E000CDD62A = {
43 | isa = PBXGroup;
44 | children = (
45 | 6866B9851C7C04E000CDD62A /* WXNearbyRadar */,
46 | 6866B9841C7C04E000CDD62A /* Products */,
47 | );
48 | sourceTree = "";
49 | };
50 | 6866B9841C7C04E000CDD62A /* Products */ = {
51 | isa = PBXGroup;
52 | children = (
53 | 6866B9831C7C04E000CDD62A /* WXNearbyRadar.app */,
54 | );
55 | name = Products;
56 | sourceTree = "";
57 | };
58 | 6866B9851C7C04E000CDD62A /* WXNearbyRadar */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 6866B9861C7C04E000CDD62A /* AppDelegate.swift */,
62 | 6866B9881C7C04E000CDD62A /* ViewController.swift */,
63 | 6866B9981C7C050800CDD62A /* RadarView.swift */,
64 | 683B28941C7D0E4600CD4FB6 /* GradientAngleCALayer.swift */,
65 | 6866B98A1C7C04E000CDD62A /* Main.storyboard */,
66 | 6866B98D1C7C04E000CDD62A /* Assets.xcassets */,
67 | 6866B98F1C7C04E000CDD62A /* LaunchScreen.storyboard */,
68 | 6866B9921C7C04E000CDD62A /* Info.plist */,
69 | );
70 | path = WXNearbyRadar;
71 | sourceTree = "";
72 | };
73 | /* End PBXGroup section */
74 |
75 | /* Begin PBXNativeTarget section */
76 | 6866B9821C7C04E000CDD62A /* WXNearbyRadar */ = {
77 | isa = PBXNativeTarget;
78 | buildConfigurationList = 6866B9951C7C04E000CDD62A /* Build configuration list for PBXNativeTarget "WXNearbyRadar" */;
79 | buildPhases = (
80 | 6866B97F1C7C04E000CDD62A /* Sources */,
81 | 6866B9801C7C04E000CDD62A /* Frameworks */,
82 | 6866B9811C7C04E000CDD62A /* Resources */,
83 | );
84 | buildRules = (
85 | );
86 | dependencies = (
87 | );
88 | name = WXNearbyRadar;
89 | productName = WXNearbyRadar;
90 | productReference = 6866B9831C7C04E000CDD62A /* WXNearbyRadar.app */;
91 | productType = "com.apple.product-type.application";
92 | };
93 | /* End PBXNativeTarget section */
94 |
95 | /* Begin PBXProject section */
96 | 6866B97B1C7C04E000CDD62A /* Project object */ = {
97 | isa = PBXProject;
98 | attributes = {
99 | LastSwiftUpdateCheck = 0720;
100 | LastUpgradeCheck = 0720;
101 | ORGANIZATIONNAME = "Xin Wu";
102 | TargetAttributes = {
103 | 6866B9821C7C04E000CDD62A = {
104 | CreatedOnToolsVersion = 7.2.1;
105 | SystemCapabilities = {
106 | com.apple.BackgroundModes = {
107 | enabled = 1;
108 | };
109 | };
110 | };
111 | };
112 | };
113 | buildConfigurationList = 6866B97E1C7C04E000CDD62A /* Build configuration list for PBXProject "WXNearbyRadar" */;
114 | compatibilityVersion = "Xcode 3.2";
115 | developmentRegion = English;
116 | hasScannedForEncodings = 0;
117 | knownRegions = (
118 | en,
119 | Base,
120 | );
121 | mainGroup = 6866B97A1C7C04E000CDD62A;
122 | productRefGroup = 6866B9841C7C04E000CDD62A /* Products */;
123 | projectDirPath = "";
124 | projectRoot = "";
125 | targets = (
126 | 6866B9821C7C04E000CDD62A /* WXNearbyRadar */,
127 | );
128 | };
129 | /* End PBXProject section */
130 |
131 | /* Begin PBXResourcesBuildPhase section */
132 | 6866B9811C7C04E000CDD62A /* Resources */ = {
133 | isa = PBXResourcesBuildPhase;
134 | buildActionMask = 2147483647;
135 | files = (
136 | 6866B9911C7C04E000CDD62A /* LaunchScreen.storyboard in Resources */,
137 | 6866B98E1C7C04E000CDD62A /* Assets.xcassets in Resources */,
138 | 6866B98C1C7C04E000CDD62A /* Main.storyboard in Resources */,
139 | );
140 | runOnlyForDeploymentPostprocessing = 0;
141 | };
142 | /* End PBXResourcesBuildPhase section */
143 |
144 | /* Begin PBXSourcesBuildPhase section */
145 | 6866B97F1C7C04E000CDD62A /* Sources */ = {
146 | isa = PBXSourcesBuildPhase;
147 | buildActionMask = 2147483647;
148 | files = (
149 | 6866B9891C7C04E000CDD62A /* ViewController.swift in Sources */,
150 | 6866B9991C7C050800CDD62A /* RadarView.swift in Sources */,
151 | 6866B9871C7C04E000CDD62A /* AppDelegate.swift in Sources */,
152 | 683B28951C7D0E4600CD4FB6 /* GradientAngleCALayer.swift in Sources */,
153 | );
154 | runOnlyForDeploymentPostprocessing = 0;
155 | };
156 | /* End PBXSourcesBuildPhase section */
157 |
158 | /* Begin PBXVariantGroup section */
159 | 6866B98A1C7C04E000CDD62A /* Main.storyboard */ = {
160 | isa = PBXVariantGroup;
161 | children = (
162 | 6866B98B1C7C04E000CDD62A /* Base */,
163 | );
164 | name = Main.storyboard;
165 | sourceTree = "";
166 | };
167 | 6866B98F1C7C04E000CDD62A /* LaunchScreen.storyboard */ = {
168 | isa = PBXVariantGroup;
169 | children = (
170 | 6866B9901C7C04E000CDD62A /* Base */,
171 | );
172 | name = LaunchScreen.storyboard;
173 | sourceTree = "";
174 | };
175 | /* End PBXVariantGroup section */
176 |
177 | /* Begin XCBuildConfiguration section */
178 | 6866B9931C7C04E000CDD62A /* Debug */ = {
179 | isa = XCBuildConfiguration;
180 | buildSettings = {
181 | ALWAYS_SEARCH_USER_PATHS = NO;
182 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
183 | CLANG_CXX_LIBRARY = "libc++";
184 | CLANG_ENABLE_MODULES = YES;
185 | CLANG_ENABLE_OBJC_ARC = YES;
186 | CLANG_WARN_BOOL_CONVERSION = YES;
187 | CLANG_WARN_CONSTANT_CONVERSION = YES;
188 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
189 | CLANG_WARN_EMPTY_BODY = YES;
190 | CLANG_WARN_ENUM_CONVERSION = YES;
191 | CLANG_WARN_INT_CONVERSION = YES;
192 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
193 | CLANG_WARN_UNREACHABLE_CODE = YES;
194 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
195 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
196 | COPY_PHASE_STRIP = NO;
197 | DEBUG_INFORMATION_FORMAT = dwarf;
198 | ENABLE_STRICT_OBJC_MSGSEND = YES;
199 | ENABLE_TESTABILITY = YES;
200 | GCC_C_LANGUAGE_STANDARD = gnu99;
201 | GCC_DYNAMIC_NO_PIC = NO;
202 | GCC_NO_COMMON_BLOCKS = YES;
203 | GCC_OPTIMIZATION_LEVEL = 0;
204 | GCC_PREPROCESSOR_DEFINITIONS = (
205 | "DEBUG=1",
206 | "$(inherited)",
207 | );
208 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
209 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
210 | GCC_WARN_UNDECLARED_SELECTOR = YES;
211 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
212 | GCC_WARN_UNUSED_FUNCTION = YES;
213 | GCC_WARN_UNUSED_VARIABLE = YES;
214 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
215 | MTL_ENABLE_DEBUG_INFO = YES;
216 | ONLY_ACTIVE_ARCH = YES;
217 | SDKROOT = iphoneos;
218 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
219 | };
220 | name = Debug;
221 | };
222 | 6866B9941C7C04E000CDD62A /* Release */ = {
223 | isa = XCBuildConfiguration;
224 | buildSettings = {
225 | ALWAYS_SEARCH_USER_PATHS = NO;
226 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
227 | CLANG_CXX_LIBRARY = "libc++";
228 | CLANG_ENABLE_MODULES = YES;
229 | CLANG_ENABLE_OBJC_ARC = YES;
230 | CLANG_WARN_BOOL_CONVERSION = YES;
231 | CLANG_WARN_CONSTANT_CONVERSION = YES;
232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
233 | CLANG_WARN_EMPTY_BODY = YES;
234 | CLANG_WARN_ENUM_CONVERSION = YES;
235 | CLANG_WARN_INT_CONVERSION = YES;
236 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
237 | CLANG_WARN_UNREACHABLE_CODE = YES;
238 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
239 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
240 | COPY_PHASE_STRIP = NO;
241 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
242 | ENABLE_NS_ASSERTIONS = NO;
243 | ENABLE_STRICT_OBJC_MSGSEND = YES;
244 | GCC_C_LANGUAGE_STANDARD = gnu99;
245 | GCC_NO_COMMON_BLOCKS = YES;
246 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
247 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
248 | GCC_WARN_UNDECLARED_SELECTOR = YES;
249 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
250 | GCC_WARN_UNUSED_FUNCTION = YES;
251 | GCC_WARN_UNUSED_VARIABLE = YES;
252 | IPHONEOS_DEPLOYMENT_TARGET = 9.2;
253 | MTL_ENABLE_DEBUG_INFO = NO;
254 | SDKROOT = iphoneos;
255 | VALIDATE_PRODUCT = YES;
256 | };
257 | name = Release;
258 | };
259 | 6866B9961C7C04E000CDD62A /* Debug */ = {
260 | isa = XCBuildConfiguration;
261 | buildSettings = {
262 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
263 | CLANG_ENABLE_MODULES = YES;
264 | INFOPLIST_FILE = WXNearbyRadar/Info.plist;
265 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
266 | PRODUCT_BUNDLE_IDENTIFIER = XinWu.WXNearbyRadar;
267 | PRODUCT_NAME = "$(TARGET_NAME)";
268 | SWIFT_INSTALL_OBJC_HEADER = NO;
269 | SWIFT_OBJC_BRIDGING_HEADER = "";
270 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
271 | };
272 | name = Debug;
273 | };
274 | 6866B9971C7C04E000CDD62A /* Release */ = {
275 | isa = XCBuildConfiguration;
276 | buildSettings = {
277 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
278 | CLANG_ENABLE_MODULES = YES;
279 | INFOPLIST_FILE = WXNearbyRadar/Info.plist;
280 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
281 | PRODUCT_BUNDLE_IDENTIFIER = XinWu.WXNearbyRadar;
282 | PRODUCT_NAME = "$(TARGET_NAME)";
283 | SWIFT_INSTALL_OBJC_HEADER = NO;
284 | SWIFT_OBJC_BRIDGING_HEADER = "";
285 | };
286 | name = Release;
287 | };
288 | /* End XCBuildConfiguration section */
289 |
290 | /* Begin XCConfigurationList section */
291 | 6866B97E1C7C04E000CDD62A /* Build configuration list for PBXProject "WXNearbyRadar" */ = {
292 | isa = XCConfigurationList;
293 | buildConfigurations = (
294 | 6866B9931C7C04E000CDD62A /* Debug */,
295 | 6866B9941C7C04E000CDD62A /* Release */,
296 | );
297 | defaultConfigurationIsVisible = 0;
298 | defaultConfigurationName = Release;
299 | };
300 | 6866B9951C7C04E000CDD62A /* Build configuration list for PBXNativeTarget "WXNearbyRadar" */ = {
301 | isa = XCConfigurationList;
302 | buildConfigurations = (
303 | 6866B9961C7C04E000CDD62A /* Debug */,
304 | 6866B9971C7C04E000CDD62A /* Release */,
305 | );
306 | defaultConfigurationIsVisible = 0;
307 | defaultConfigurationName = Release;
308 | };
309 | /* End XCConfigurationList section */
310 | };
311 | rootObject = 6866B97B1C7C04E000CDD62A /* Project object */;
312 | }
313 |
--------------------------------------------------------------------------------
/WXNearbyRadar.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WXNearbyRadar.xcodeproj/project.xcworkspace/xcuserdata/xinwu.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wx0165927473/WXNearbyRadar/d954cf6455d0b9769dc9c515459cafbd503a48f8/WXNearbyRadar.xcodeproj/project.xcworkspace/xcuserdata/xinwu.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/WXNearbyRadar.xcodeproj/xcuserdata/xinwu.xcuserdatad/xcschemes/WXNearbyRadar.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/WXNearbyRadar.xcodeproj/xcuserdata/xinwu.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | WXNearbyRadar.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 6866B9821C7C04E000CDD62A
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/WXNearbyRadar/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // WXNearbyRadar
4 | //
5 | // Created by Xin Wu on 2/22/16.
6 | // Copyright © 2016 Xin Wu. 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: [NSObject: AnyObject]?) -> 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 throttle down OpenGL ES frame rates. 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 inactive 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 |
--------------------------------------------------------------------------------
/WXNearbyRadar/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/WXNearbyRadar/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/WXNearbyRadar/Assets.xcassets/avatar.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "avatar.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/WXNearbyRadar/Assets.xcassets/avatar.imageset/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wx0165927473/WXNearbyRadar/d954cf6455d0b9769dc9c515459cafbd503a48f8/WXNearbyRadar/Assets.xcassets/avatar.imageset/avatar.png
--------------------------------------------------------------------------------
/WXNearbyRadar/Base.lproj/LaunchScreen.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 |
--------------------------------------------------------------------------------
/WXNearbyRadar/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/WXNearbyRadar/GradientAngleCALayer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GradientAngleCALayer.swift
3 | // WXNearbyRadar
4 | //
5 | // Created by Xin Wu on 2/23/16.
6 | // Copyright © 2016 Xin Wu. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class GradientAngleCALayer: CALayer {
12 |
13 | private struct Constants {
14 | static let MaxAngle = 2 * M_PI
15 | static let MaxHue = 255.0
16 | }
17 |
18 | private struct Transition {
19 | let fromLocation: Double
20 | let toLocation: Double
21 | let fromColor: UIColor
22 | let toColor: UIColor
23 |
24 | func colorForPercent(percent: Double) -> UIColor {
25 | let normalizedPercent = percent.convertFromRange(min: fromLocation, max: toLocation, toRangeMin: 0.0, max: 1.0)
26 | return UIColor.lerp(from: fromColor.rgba, to: toColor.rgba, percent: CGFloat(normalizedPercent))
27 | }
28 | }
29 |
30 | // MARK: - Properties
31 |
32 | /// The array of UIColor objects defining the color of each gradient stop.
33 | /// Defaults to empty array. Animatable.
34 |
35 | internal var colors = [UIColor]() { didSet { setNeedsDisplay() } }
36 |
37 | /// The array of Double values defining the location of each
38 | /// gradient stop as a value in the range [0,1]. The values must be
39 | /// monotonically increasing. If empty array is given, the stops are
40 | /// assumed to spread uniformly across the [0,1] range.
41 | /// Defaults to nil. Animatable.
42 |
43 | internal var locations = [Double]() { didSet { setNeedsDisplay() } }
44 |
45 | private var transitions = [Transition]()
46 |
47 | internal override func drawInContext(ctx: CGContext) {
48 | UIGraphicsPushContext(ctx)
49 | drawRect(CGContextGetClipBoundingBox(ctx))
50 | UIGraphicsPopContext()
51 | }
52 |
53 | private func drawRect(rect: CGRect) {
54 | loadTransitions()
55 |
56 | let center = CGPoint(x: rect.midX, y: rect.midY)
57 | let longerSide = max(rect.width, rect.height)
58 | let radius = Double(longerSide) * M_SQRT2
59 | var angle = 0.0
60 | let step = M_PI_2 / radius
61 |
62 | while angle <= Constants.MaxAngle {
63 | let pointX = radius * cos(angle) + Double(center.x)
64 | let pointY = radius * sin(angle) + Double(center.y)
65 | let startPoint = CGPoint(x: pointX, y: pointY)
66 |
67 | let line = UIBezierPath()
68 | line.moveToPoint(startPoint)
69 | line.addLineToPoint(center)
70 |
71 | colorForAngle(angle).setStroke()
72 | line.stroke()
73 |
74 | angle += step
75 | }
76 | }
77 |
78 | private func colorForAngle(angle: Double) -> UIColor {
79 | let percent = angle.convertFromRangeZeroToMax(Constants.MaxAngle, toRangeZeroToMax: 1.0)
80 | guard let transition = transitionForPercent(percent) else { return spectrumColorForAngle(angle) }
81 | return transition.colorForPercent(percent)
82 | }
83 |
84 | private func spectrumColorForAngle(angle: Double) -> UIColor {
85 | let hue = angle.convertFromRangeZeroToMax(Constants.MaxAngle, toRangeZeroToMax: Constants.MaxHue)
86 | return UIColor(hue: CGFloat(hue / Constants.MaxHue), saturation: 1.0, brightness: 1.0, alpha: 1.0)
87 | }
88 |
89 | private func loadTransitions() {
90 | transitions.removeAll()
91 |
92 | if colors.count > 1 {
93 | let transitionsCount = colors.count - 1
94 | let locationStep = 1.0 / Double(transitionsCount)
95 |
96 | for i in 0 ..< transitionsCount {
97 | let fromLocation, toLocation: Double
98 | let fromColor, toColor: UIColor
99 |
100 | if locations.count == colors.count {
101 | fromLocation = locations[i]
102 | toLocation = locations[i + 1]
103 | } else {
104 | fromLocation = locationStep * Double(i)
105 | toLocation = locationStep * Double(i + 1)
106 | }
107 |
108 | fromColor = colors[i]
109 | toColor = colors[i + 1]
110 |
111 | let transition = Transition(fromLocation: fromLocation, toLocation: toLocation, fromColor: fromColor, toColor: toColor)
112 | transitions.append(transition)
113 | }
114 | }
115 | }
116 |
117 | private func transitionForPercent(percent: Double) -> Transition? {
118 | let filtered = transitions.filter { percent >= $0.fromLocation && percent < $0.toLocation }
119 | let defaultTransition = percent <= 0.5 ? transitions.first : transitions.last
120 | return filtered.first ?? defaultTransition
121 | }
122 |
123 | }
124 |
125 | // MARK: - Extensions
126 |
127 | private extension Double {
128 |
129 | func convertFromRange(min oldMin: Double, max oldMax: Double, toRangeMin newMin: Double, max newMax: Double) -> Double {
130 | let oldRange, newRange, newValue: Double
131 | oldRange = (oldMax - oldMin)
132 | if (oldRange == 0.0) {
133 | newValue = newMin
134 | } else {
135 | newRange = (newMax - newMin)
136 | newValue = (((self - oldMin) * newRange) / oldRange) + newMin
137 | }
138 | return newValue
139 | }
140 |
141 | func convertFromRangeZeroToMax(currentMaxValue: Double, toRangeZeroToMax newMaxValue: Double) -> Double {
142 | return ((self * newMaxValue) / currentMaxValue)
143 | }
144 |
145 | }
146 |
147 | private extension UIColor {
148 |
149 | struct RGBA {
150 | var red: CGFloat = 0.0
151 | var green: CGFloat = 0.0
152 | var blue: CGFloat = 0.0
153 | var alpha: CGFloat = 0.0
154 |
155 | init(color: UIColor) {
156 | color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
157 | }
158 | }
159 |
160 | var rgba: RGBA {
161 | return RGBA(color: self)
162 | }
163 |
164 | class func lerp(from from: UIColor.RGBA, to: UIColor.RGBA, percent: CGFloat) -> UIColor {
165 | let red = from.red + percent * (to.red - from.red)
166 | let green = from.green + percent * (to.green - from.green)
167 | let blue = from.blue + percent * (to.blue - from.blue)
168 | let alpha = from.alpha + percent * (to.alpha - from.alpha)
169 | return UIColor(red: red, green: green, blue: blue, alpha: alpha)
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/WXNearbyRadar/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSLocationWhenInUsageDescription
6 |
7 | NSLocationAlwaysUsageDescription
8 |
9 | CFBundleDevelopmentRegion
10 | en
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | $(PRODUCT_NAME)
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | 1.0
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | 1
27 | LSRequiresIPhoneOS
28 |
29 | UIBackgroundModes
30 |
31 | location
32 |
33 | UILaunchStoryboardName
34 | LaunchScreen
35 | UIMainStoryboardFile
36 | Main
37 | UIRequiredDeviceCapabilities
38 |
39 | armv7
40 |
41 | UISupportedInterfaceOrientations
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/WXNearbyRadar/RadarView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Radar.swift
3 | // RadarAnim
4 | //
5 | // Created by Xin Wu on 2/21/16.
6 | // Copyright © 2016 Xin Wu. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class RadarView: UIView {
12 |
13 | /*
14 | // Only override drawRect: if you perform custom drawing.
15 | // An empty implementation adversely affects performance during animation.
16 | override func drawRect(rect: CGRect) {
17 | // Drawing code
18 | }
19 | */
20 |
21 | override class func layerClass() -> AnyClass {
22 | return GradientAngleCALayer.classForCoder()
23 | }
24 |
25 | override init(frame: CGRect) {
26 | super.init(frame: frame)
27 |
28 | self.backgroundColor = UIColor.clearColor()
29 | self.opaque = false
30 | // Change the color of scan animation
31 | let colors = [UIColor(red: 1, green: 1, blue: 1, alpha: 0),
32 | UIColor(red: 1, green: 1, blue: 1, alpha: 0),
33 | UIColor(red: 1, green: 1, blue: 1, alpha: 0),
34 | UIColor(red: 192/255, green: 57/255, blue: 43/255, alpha: 1.0)]
35 |
36 | // AngleGradientLayer by Pavel Ivashkov
37 | let l = self.layer as! GradientAngleCALayer
38 | l.colors = colors
39 |
40 | // Since our gradient layer is built as an image,
41 | // we need to scale it to match the display of the device.
42 | l.contentsScale = UIScreen.mainScreen().scale
43 |
44 | l.cornerRadius = CGRectGetWidth(self.bounds) / 2
45 |
46 | self.clipsToBounds = true
47 | self.userInteractionEnabled = false
48 |
49 | }
50 |
51 | required init?(coder aDecoder: NSCoder) {
52 | fatalError("init(coder:) has not been implemented")
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/WXNearbyRadar/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // WXNearbyRadar
4 | //
5 | // Created by Xin Wu on 2/22/16.
6 | // Copyright © 2016 Xin Wu. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import MapKit
11 |
12 | class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
13 |
14 |
15 | let locManager = CLLocationManager()
16 | let mapView = MKMapView()
17 | var radarView : RadarView!
18 | var userAvatarImageView : UIImageView!
19 |
20 | override func viewDidLoad() {
21 | super.viewDidLoad()
22 |
23 | // Location Manager Init
24 | locManager.delegate = self
25 | locManager.desiredAccuracy = kCLLocationAccuracyBest
26 | locManager.requestAlwaysAuthorization()
27 | locManager.startUpdatingLocation()
28 |
29 | // Map View Init
30 | mapView.frame = CGRectMake(10, 100, self.view.frame.width - 20, self.view.frame.width - 20)
31 | mapView.delegate = self
32 | mapView.userInteractionEnabled = false
33 | mapView.zoomEnabled = false
34 | mapView.layer.cornerRadius = mapView.frame.height / 2
35 | self.view.addSubview(mapView)
36 |
37 | // Radar View Init
38 | radarView = RadarView.init(frame: CGRectMake(0, 0, mapView.frame.size.width, mapView.frame.size.height))
39 | radarView.layer.contentsScale = UIScreen.mainScreen().scale
40 | radarView.alpha = 0.8
41 | mapView.addSubview(radarView)
42 |
43 | // User Avatar Init
44 | userAvatarImageView = UIImageView(image: UIImage(named: "avatar"))
45 | userAvatarImageView.backgroundColor = UIColor.grayColor()
46 | userAvatarImageView.frame.size = CGSizeMake(mapView.frame.width / 3.5, mapView.frame.height / 3.5)
47 | userAvatarImageView.frame.origin = CGPointMake(mapView.center.x - userAvatarImageView.frame.width / 2, mapView.center.y - userAvatarImageView.frame.height / 2)
48 | userAvatarImageView.layer.cornerRadius = userAvatarImageView.bounds.width / 2
49 | userAvatarImageView.layer.borderWidth = 3
50 | userAvatarImageView.layer.borderColor = UIColor.whiteColor().CGColor
51 | userAvatarImageView.clipsToBounds = true
52 | self.view.addSubview(userAvatarImageView)
53 |
54 | // Add ripple animation to avatar
55 | let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:Selector("tapAvatarImageAction:"))
56 | userAvatarImageView.userInteractionEnabled = true
57 | userAvatarImageView.addGestureRecognizer(tapGestureRecognizer)
58 |
59 | // Start spinning the radar and ripple anim
60 | spinRadar()
61 | NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: Selector("tapAvatarImageAction:"), userInfo: nil, repeats: true)
62 |
63 | }
64 |
65 | override func didReceiveMemoryWarning() {
66 | super.didReceiveMemoryWarning()
67 | // Dispose of any resources that can be recreated.
68 | }
69 |
70 | // MARK: Radar Animation
71 | func spinRadar() {
72 | let spin = CABasicAnimation(keyPath: "transform.rotation")
73 | // Change spinning speed here
74 | spin.duration = 2
75 | spin.toValue = NSNumber(double: M_PI)
76 | spin.cumulative = true
77 | spin.removedOnCompletion = false
78 | spin.repeatCount = MAXFLOAT;
79 | radarView.layer.addAnimation(spin, forKey: "spinRadarView")
80 | }
81 |
82 | // MARK: Location Delegate Methods
83 | func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
84 | let location = locations.last
85 |
86 | let center = CLLocationCoordinate2D(latitude: (location?.coordinate.latitude)!, longitude: (location?.coordinate.longitude)!)
87 |
88 | // Change how detail you want the map to show (smaller means more detailed)
89 | let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01))
90 |
91 | mapView.setRegion(region, animated: false)
92 | locManager.stopUpdatingLocation()
93 | }
94 |
95 | func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
96 | print("Error: " + error.localizedDescription)
97 | }
98 |
99 | // MARK: Ripple Animation
100 | func tapAvatarImageAction(img: AnyObject) {
101 | let stroke = UIColor(red: 192/255, green: 57/255, blue: 43/255, alpha: 1.0)
102 | let pathFrame = CGRectMake(-CGRectGetMidX(userAvatarImageView.bounds), -CGRectGetMidY(userAvatarImageView.bounds), userAvatarImageView.bounds.size.width, userAvatarImageView.bounds.size.height)
103 | let path = UIBezierPath(roundedRect: pathFrame, cornerRadius: mapView.layer.cornerRadius)
104 | let shapePosition = mapView.convertPoint(mapView.center, fromView: nil)
105 |
106 | let circleShape = CAShapeLayer()
107 | circleShape.path = path.CGPath
108 | circleShape.position = shapePosition
109 | circleShape.fillColor = UIColor.clearColor().CGColor
110 | circleShape.opacity = 0
111 | circleShape.strokeColor = stroke.CGColor
112 | circleShape.lineWidth = 1.3
113 | mapView.layer.addSublayer(circleShape)
114 |
115 | CATransaction.begin()
116 | CATransaction.setCompletionBlock { () -> Void in
117 | circleShape.removeFromSuperlayer()
118 | }
119 |
120 | let scaleAnimation = CABasicAnimation(keyPath: "transform.scale")
121 | scaleAnimation.fromValue = NSValue(CATransform3D: CATransform3DIdentity)
122 | scaleAnimation.toValue = NSValue(CATransform3D: CATransform3DMakeScale(3.5, 3.5, 1))
123 |
124 | let alphaAnimation = CABasicAnimation(keyPath: "opacity")
125 | alphaAnimation.fromValue = 1
126 | alphaAnimation.toValue = 0
127 |
128 | let animation = CAAnimationGroup()
129 | animation.animations = [scaleAnimation, alphaAnimation]
130 | animation.duration = 1
131 | animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
132 | circleShape.addAnimation(animation, forKey: nil)
133 |
134 | CATransaction.commit()
135 | }
136 |
137 | }
138 |
139 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wx0165927473/WXNearbyRadar/d954cf6455d0b9769dc9c515459cafbd503a48f8/demo.gif
--------------------------------------------------------------------------------