├── ZoomVPN ├── Assets.xcassets │ ├── Contents.json │ ├── download.imageset │ │ ├── download.png │ │ ├── download@2x.png │ │ ├── download@3x.png │ │ └── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── ZoomVPNApp.swift ├── Info.plist └── ContentView.swift ├── ZoomVPN.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ └── Anik.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── Anik.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj └── README.md /ZoomVPN/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ZoomVPN/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ZoomVPN/Assets.xcassets/download.imageset/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kazimunshimun/VPNAppUI/HEAD/ZoomVPN/Assets.xcassets/download.imageset/download.png -------------------------------------------------------------------------------- /ZoomVPN/Assets.xcassets/download.imageset/download@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kazimunshimun/VPNAppUI/HEAD/ZoomVPN/Assets.xcassets/download.imageset/download@2x.png -------------------------------------------------------------------------------- /ZoomVPN/Assets.xcassets/download.imageset/download@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kazimunshimun/VPNAppUI/HEAD/ZoomVPN/Assets.xcassets/download.imageset/download@3x.png -------------------------------------------------------------------------------- /ZoomVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ZoomVPN/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ZoomVPN.xcodeproj/project.xcworkspace/xcuserdata/Anik.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kazimunshimun/VPNAppUI/HEAD/ZoomVPN.xcodeproj/project.xcworkspace/xcuserdata/Anik.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VPNAppUI 2 | 3 | #### Youtube Speedcode tutorial: 4 | [![VPNAppUI tutorial](http://img.youtube.com/vi/i8gouuSNIqg/0.jpg)](https://youtu.be/i8gouuSNIqg) 5 | #### Dribble Inspiration: 6 | https://dribbble.com/shots/13818562-VPN-Interaction-Concept 7 | -------------------------------------------------------------------------------- /ZoomVPN/ZoomVPNApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ZoomVPNApp.swift 3 | // ZoomVPN 4 | // 5 | // Created by Anik on 22/10/20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct ZoomVPNApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ZoomVPN.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ZoomVPN.xcodeproj/xcuserdata/Anik.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ZoomVPN.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ZoomVPN/Assets.xcassets/download.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "download.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "download@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "download@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ZoomVPN/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | 28 | UIApplicationSupportsIndirectInputEvents 29 | 30 | UILaunchScreen 31 | 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /ZoomVPN/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ZoomVPN.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 84D618CD2541A4DC0018B24B /* ZoomVPNApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D618CC2541A4DC0018B24B /* ZoomVPNApp.swift */; }; 11 | 84D618CF2541A4DC0018B24B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84D618CE2541A4DC0018B24B /* ContentView.swift */; }; 12 | 84D618D12541A4E00018B24B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84D618D02541A4E00018B24B /* Assets.xcassets */; }; 13 | 84D618D42541A4E00018B24B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84D618D32541A4E00018B24B /* Preview Assets.xcassets */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | 84D618C92541A4DC0018B24B /* ZoomVPN.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZoomVPN.app; sourceTree = BUILT_PRODUCTS_DIR; }; 18 | 84D618CC2541A4DC0018B24B /* ZoomVPNApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZoomVPNApp.swift; sourceTree = ""; }; 19 | 84D618CE2541A4DC0018B24B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 20 | 84D618D02541A4E00018B24B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 21 | 84D618D32541A4E00018B24B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 22 | 84D618D52541A4E00018B24B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 23 | /* End PBXFileReference section */ 24 | 25 | /* Begin PBXFrameworksBuildPhase section */ 26 | 84D618C62541A4DC0018B24B /* Frameworks */ = { 27 | isa = PBXFrameworksBuildPhase; 28 | buildActionMask = 2147483647; 29 | files = ( 30 | ); 31 | runOnlyForDeploymentPostprocessing = 0; 32 | }; 33 | /* End PBXFrameworksBuildPhase section */ 34 | 35 | /* Begin PBXGroup section */ 36 | 84D618C02541A4DC0018B24B = { 37 | isa = PBXGroup; 38 | children = ( 39 | 84D618CB2541A4DC0018B24B /* ZoomVPN */, 40 | 84D618CA2541A4DC0018B24B /* Products */, 41 | ); 42 | sourceTree = ""; 43 | }; 44 | 84D618CA2541A4DC0018B24B /* Products */ = { 45 | isa = PBXGroup; 46 | children = ( 47 | 84D618C92541A4DC0018B24B /* ZoomVPN.app */, 48 | ); 49 | name = Products; 50 | sourceTree = ""; 51 | }; 52 | 84D618CB2541A4DC0018B24B /* ZoomVPN */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 84D618CC2541A4DC0018B24B /* ZoomVPNApp.swift */, 56 | 84D618CE2541A4DC0018B24B /* ContentView.swift */, 57 | 84D618D02541A4E00018B24B /* Assets.xcassets */, 58 | 84D618D52541A4E00018B24B /* Info.plist */, 59 | 84D618D22541A4E00018B24B /* Preview Content */, 60 | ); 61 | path = ZoomVPN; 62 | sourceTree = ""; 63 | }; 64 | 84D618D22541A4E00018B24B /* Preview Content */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 84D618D32541A4E00018B24B /* Preview Assets.xcassets */, 68 | ); 69 | path = "Preview Content"; 70 | sourceTree = ""; 71 | }; 72 | /* End PBXGroup section */ 73 | 74 | /* Begin PBXNativeTarget section */ 75 | 84D618C82541A4DC0018B24B /* ZoomVPN */ = { 76 | isa = PBXNativeTarget; 77 | buildConfigurationList = 84D618D82541A4E00018B24B /* Build configuration list for PBXNativeTarget "ZoomVPN" */; 78 | buildPhases = ( 79 | 84D618C52541A4DC0018B24B /* Sources */, 80 | 84D618C62541A4DC0018B24B /* Frameworks */, 81 | 84D618C72541A4DC0018B24B /* Resources */, 82 | ); 83 | buildRules = ( 84 | ); 85 | dependencies = ( 86 | ); 87 | name = ZoomVPN; 88 | productName = ZoomVPN; 89 | productReference = 84D618C92541A4DC0018B24B /* ZoomVPN.app */; 90 | productType = "com.apple.product-type.application"; 91 | }; 92 | /* End PBXNativeTarget section */ 93 | 94 | /* Begin PBXProject section */ 95 | 84D618C12541A4DC0018B24B /* Project object */ = { 96 | isa = PBXProject; 97 | attributes = { 98 | LastSwiftUpdateCheck = 1200; 99 | LastUpgradeCheck = 1200; 100 | TargetAttributes = { 101 | 84D618C82541A4DC0018B24B = { 102 | CreatedOnToolsVersion = 12.0.1; 103 | }; 104 | }; 105 | }; 106 | buildConfigurationList = 84D618C42541A4DC0018B24B /* Build configuration list for PBXProject "ZoomVPN" */; 107 | compatibilityVersion = "Xcode 9.3"; 108 | developmentRegion = en; 109 | hasScannedForEncodings = 0; 110 | knownRegions = ( 111 | en, 112 | Base, 113 | ); 114 | mainGroup = 84D618C02541A4DC0018B24B; 115 | productRefGroup = 84D618CA2541A4DC0018B24B /* Products */; 116 | projectDirPath = ""; 117 | projectRoot = ""; 118 | targets = ( 119 | 84D618C82541A4DC0018B24B /* ZoomVPN */, 120 | ); 121 | }; 122 | /* End PBXProject section */ 123 | 124 | /* Begin PBXResourcesBuildPhase section */ 125 | 84D618C72541A4DC0018B24B /* Resources */ = { 126 | isa = PBXResourcesBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | 84D618D42541A4E00018B24B /* Preview Assets.xcassets in Resources */, 130 | 84D618D12541A4E00018B24B /* Assets.xcassets in Resources */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | /* End PBXResourcesBuildPhase section */ 135 | 136 | /* Begin PBXSourcesBuildPhase section */ 137 | 84D618C52541A4DC0018B24B /* Sources */ = { 138 | isa = PBXSourcesBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | 84D618CF2541A4DC0018B24B /* ContentView.swift in Sources */, 142 | 84D618CD2541A4DC0018B24B /* ZoomVPNApp.swift in Sources */, 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | /* End PBXSourcesBuildPhase section */ 147 | 148 | /* Begin XCBuildConfiguration section */ 149 | 84D618D62541A4E00018B24B /* Debug */ = { 150 | isa = XCBuildConfiguration; 151 | buildSettings = { 152 | ALWAYS_SEARCH_USER_PATHS = NO; 153 | CLANG_ANALYZER_NONNULL = YES; 154 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 155 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 156 | CLANG_CXX_LIBRARY = "libc++"; 157 | CLANG_ENABLE_MODULES = YES; 158 | CLANG_ENABLE_OBJC_ARC = YES; 159 | CLANG_ENABLE_OBJC_WEAK = YES; 160 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 161 | CLANG_WARN_BOOL_CONVERSION = YES; 162 | CLANG_WARN_COMMA = YES; 163 | CLANG_WARN_CONSTANT_CONVERSION = YES; 164 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 165 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 166 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 167 | CLANG_WARN_EMPTY_BODY = YES; 168 | CLANG_WARN_ENUM_CONVERSION = YES; 169 | CLANG_WARN_INFINITE_RECURSION = YES; 170 | CLANG_WARN_INT_CONVERSION = YES; 171 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 172 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 173 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 174 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 175 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 176 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 177 | CLANG_WARN_STRICT_PROTOTYPES = YES; 178 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 179 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 180 | CLANG_WARN_UNREACHABLE_CODE = YES; 181 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 182 | COPY_PHASE_STRIP = NO; 183 | DEBUG_INFORMATION_FORMAT = dwarf; 184 | ENABLE_STRICT_OBJC_MSGSEND = YES; 185 | ENABLE_TESTABILITY = YES; 186 | GCC_C_LANGUAGE_STANDARD = gnu11; 187 | GCC_DYNAMIC_NO_PIC = NO; 188 | GCC_NO_COMMON_BLOCKS = YES; 189 | GCC_OPTIMIZATION_LEVEL = 0; 190 | GCC_PREPROCESSOR_DEFINITIONS = ( 191 | "DEBUG=1", 192 | "$(inherited)", 193 | ); 194 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 195 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 196 | GCC_WARN_UNDECLARED_SELECTOR = YES; 197 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 198 | GCC_WARN_UNUSED_FUNCTION = YES; 199 | GCC_WARN_UNUSED_VARIABLE = YES; 200 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 201 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 202 | MTL_FAST_MATH = YES; 203 | ONLY_ACTIVE_ARCH = YES; 204 | SDKROOT = iphoneos; 205 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 206 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 207 | }; 208 | name = Debug; 209 | }; 210 | 84D618D72541A4E00018B24B /* Release */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | CLANG_ANALYZER_NONNULL = YES; 215 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 217 | CLANG_CXX_LIBRARY = "libc++"; 218 | CLANG_ENABLE_MODULES = YES; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_ENABLE_OBJC_WEAK = YES; 221 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 222 | CLANG_WARN_BOOL_CONVERSION = YES; 223 | CLANG_WARN_COMMA = YES; 224 | CLANG_WARN_CONSTANT_CONVERSION = YES; 225 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 226 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 227 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 228 | CLANG_WARN_EMPTY_BODY = YES; 229 | CLANG_WARN_ENUM_CONVERSION = YES; 230 | CLANG_WARN_INFINITE_RECURSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 233 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 234 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 237 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 238 | CLANG_WARN_STRICT_PROTOTYPES = YES; 239 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 240 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 241 | CLANG_WARN_UNREACHABLE_CODE = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | COPY_PHASE_STRIP = NO; 244 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 245 | ENABLE_NS_ASSERTIONS = NO; 246 | ENABLE_STRICT_OBJC_MSGSEND = YES; 247 | GCC_C_LANGUAGE_STANDARD = gnu11; 248 | GCC_NO_COMMON_BLOCKS = YES; 249 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 250 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 251 | GCC_WARN_UNDECLARED_SELECTOR = YES; 252 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 253 | GCC_WARN_UNUSED_FUNCTION = YES; 254 | GCC_WARN_UNUSED_VARIABLE = YES; 255 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 256 | MTL_ENABLE_DEBUG_INFO = NO; 257 | MTL_FAST_MATH = YES; 258 | SDKROOT = iphoneos; 259 | SWIFT_COMPILATION_MODE = wholemodule; 260 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 261 | VALIDATE_PRODUCT = YES; 262 | }; 263 | name = Release; 264 | }; 265 | 84D618D92541A4E00018B24B /* Debug */ = { 266 | isa = XCBuildConfiguration; 267 | buildSettings = { 268 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 269 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 270 | CODE_SIGN_STYLE = Automatic; 271 | DEVELOPMENT_ASSET_PATHS = "\"ZoomVPN/Preview Content\""; 272 | DEVELOPMENT_TEAM = 3EYUW49QFV; 273 | ENABLE_PREVIEWS = YES; 274 | INFOPLIST_FILE = ZoomVPN/Info.plist; 275 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 276 | LD_RUNPATH_SEARCH_PATHS = ( 277 | "$(inherited)", 278 | "@executable_path/Frameworks", 279 | ); 280 | PRODUCT_BUNDLE_IDENTIFIER = com.a7studio.ZoomVPN; 281 | PRODUCT_NAME = "$(TARGET_NAME)"; 282 | SWIFT_VERSION = 5.0; 283 | TARGETED_DEVICE_FAMILY = "1,2"; 284 | }; 285 | name = Debug; 286 | }; 287 | 84D618DA2541A4E00018B24B /* Release */ = { 288 | isa = XCBuildConfiguration; 289 | buildSettings = { 290 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 291 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 292 | CODE_SIGN_STYLE = Automatic; 293 | DEVELOPMENT_ASSET_PATHS = "\"ZoomVPN/Preview Content\""; 294 | DEVELOPMENT_TEAM = 3EYUW49QFV; 295 | ENABLE_PREVIEWS = YES; 296 | INFOPLIST_FILE = ZoomVPN/Info.plist; 297 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 298 | LD_RUNPATH_SEARCH_PATHS = ( 299 | "$(inherited)", 300 | "@executable_path/Frameworks", 301 | ); 302 | PRODUCT_BUNDLE_IDENTIFIER = com.a7studio.ZoomVPN; 303 | PRODUCT_NAME = "$(TARGET_NAME)"; 304 | SWIFT_VERSION = 5.0; 305 | TARGETED_DEVICE_FAMILY = "1,2"; 306 | }; 307 | name = Release; 308 | }; 309 | /* End XCBuildConfiguration section */ 310 | 311 | /* Begin XCConfigurationList section */ 312 | 84D618C42541A4DC0018B24B /* Build configuration list for PBXProject "ZoomVPN" */ = { 313 | isa = XCConfigurationList; 314 | buildConfigurations = ( 315 | 84D618D62541A4E00018B24B /* Debug */, 316 | 84D618D72541A4E00018B24B /* Release */, 317 | ); 318 | defaultConfigurationIsVisible = 0; 319 | defaultConfigurationName = Release; 320 | }; 321 | 84D618D82541A4E00018B24B /* Build configuration list for PBXNativeTarget "ZoomVPN" */ = { 322 | isa = XCConfigurationList; 323 | buildConfigurations = ( 324 | 84D618D92541A4E00018B24B /* Debug */, 325 | 84D618DA2541A4E00018B24B /* Release */, 326 | ); 327 | defaultConfigurationIsVisible = 0; 328 | defaultConfigurationName = Release; 329 | }; 330 | /* End XCConfigurationList section */ 331 | }; 332 | rootObject = 84D618C12541A4DC0018B24B /* Project object */; 333 | } 334 | -------------------------------------------------------------------------------- /ZoomVPN/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // ZoomVPN 4 | // 5 | // Created by Anik on 22/10/20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | var body: some View { 12 | HomeView() 13 | } 14 | } 15 | 16 | struct HomeView: View { 17 | @State var showSideMenu = false 18 | @StateObject var speedSimulator = SpeedSimultor() 19 | var body: some View { 20 | ZStack { 21 | Color.appPrimary 22 | .ignoresSafeArea() 23 | 24 | VStack { 25 | // top menu view 26 | TopMenuView(showSideMenu: $showSideMenu) 27 | // speed text view 28 | SpeedTextView(speedSimulator: speedSimulator) 29 | 30 | Spacer() 31 | // Progress view 32 | ProgressView(speedSimulator: speedSimulator) 33 | 34 | Spacer() 35 | // start stop button view 36 | Button(action: { 37 | // start speed simulation 38 | speedSimulator.startSpeedTest() 39 | }, label: { 40 | StartStopButtonView(speedSimulator: speedSimulator) 41 | }) 42 | 43 | Spacer() 44 | } 45 | .foregroundColor(.white) 46 | .padding(.horizontal) 47 | 48 | VStack { 49 | Spacer() 50 | // drop down view 51 | DropdownView() 52 | .padding(.horizontal, 30) 53 | } 54 | 55 | if showSideMenu { 56 | SideMenuView(showSideMenu: $showSideMenu) 57 | .foregroundColor(.white) 58 | } 59 | 60 | } 61 | } 62 | } 63 | 64 | struct TopMenuView: View { 65 | @Binding var showSideMenu: Bool 66 | var body: some View { 67 | HStack { 68 | Button(action: { 69 | //show side menu 70 | showSideMenu = true 71 | }, label: { 72 | VStack { 73 | HStack { 74 | Circle() 75 | .frame(width: 6, height: 6) 76 | Circle() 77 | .frame(width: 6, height: 6) 78 | } 79 | 80 | HStack { 81 | Circle() 82 | .frame(width: 6, height: 6) 83 | Circle() 84 | .frame(width: 6, height: 6) 85 | } 86 | } 87 | .padding() 88 | }) 89 | 90 | Text("ZOOM") 91 | .font(.system(size: 18, weight: .black)) 92 | 93 | Text("VPN") 94 | .font(.system(size: 18, weight: .regular)) 95 | 96 | Spacer() 97 | 98 | PremiumView() 99 | } 100 | 101 | } 102 | } 103 | 104 | struct PremiumView: View { 105 | var fillRect = true 106 | var body: some View { 107 | ZStack { 108 | if fillRect { 109 | RoundedRectangle(cornerRadius: 20.0) 110 | .fill(Color.darkPurple) 111 | .frame(width: 135, height: 40) 112 | } else { 113 | RoundedRectangle(cornerRadius: 20.0) 114 | .stroke(Color.darkPurple) 115 | .frame(width: 135, height: 40) 116 | } 117 | 118 | HStack { 119 | Image(systemName: "flame.fill") 120 | Text("GO PREMIUM") 121 | .font(.system(size: 12, weight: .regular)) 122 | } 123 | } 124 | } 125 | } 126 | 127 | struct SpeedTextView: View { 128 | @ObservedObject var speedSimulator: SpeedSimultor 129 | var body: some View { 130 | ZStack { 131 | EmitterView(width: UIScreen.screenWidth, height: 80) 132 | .opacity(speedSimulator.start ? 1.0 : 0.0) 133 | .frame(height: 80) 134 | VStack { 135 | Text(String(format: "%.2f", speedSimulator.calculatedSpeed)) 136 | .font(.system(size: 40, weight: .semibold)) 137 | .animation(.none) 138 | 139 | Text("mb/s") 140 | .font(.system(size: 16, weight: .light)) 141 | } 142 | } 143 | } 144 | } 145 | 146 | struct EmitterView: UIViewRepresentable { 147 | let width: CGFloat 148 | let height: CGFloat 149 | 150 | func makeUIView(context: Context) -> some UIView { 151 | let size = CGSize(width: width, height: height) 152 | let host = UIView(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height)) 153 | 154 | let emitterLayer = CAEmitterLayer() 155 | emitterLayer.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height) 156 | 157 | host.layer.addSublayer(emitterLayer) 158 | host.layer.masksToBounds = true 159 | 160 | emitterLayer.emitterShape = .circle 161 | emitterLayer.emitterPosition = CGPoint(x: size.width/2, y: size.height/2) 162 | emitterLayer.emitterSize = size 163 | 164 | let emitterCell = EmitterCell().content(.circle(20)) 165 | emitterCell.color = UIColor.white.cgColor 166 | emitterCell.birthRate = 25 167 | emitterCell.lifetime = 4.0 168 | emitterCell.scale = 0.01 169 | emitterCell.alphaRange = 0.1 170 | emitterCell.alphaSpeed = 0.3 171 | 172 | emitterLayer.emitterCells = [emitterCell] 173 | 174 | return host 175 | } 176 | 177 | func updateUIView(_ uiView: UIViewType, context: Context) { 178 | 179 | } 180 | } 181 | 182 | fileprivate extension EmitterCell.Content { 183 | var image: UIImage { 184 | switch self { 185 | case let .image(image): 186 | return image 187 | case let .circle(radius): 188 | let size = CGSize(width: radius * 2, height: radius * 2) 189 | return UIGraphicsImageRenderer(size: size).image { context in 190 | context.cgContext.setFillColor(UIColor.white.cgColor) 191 | context.cgContext.addPath(CGPath(ellipseIn: CGRect(origin: .zero, size: size), transform: nil)) 192 | context.cgContext.fillPath() 193 | } 194 | } 195 | } 196 | } 197 | 198 | class EmitterCell: CAEmitterCell { 199 | override init() { 200 | super.init() 201 | } 202 | 203 | required init?(coder: NSCoder) { 204 | super.init(coder: coder) 205 | } 206 | 207 | public enum Content { 208 | case image(UIImage) 209 | case circle(CGFloat) 210 | } 211 | 212 | dynamic func content(_ content: Content) -> Self { 213 | self.contents = content.image.cgImage 214 | return self 215 | } 216 | } 217 | 218 | struct ProgressView: View { 219 | @ObservedObject var speedSimulator: SpeedSimultor 220 | var body: some View { 221 | ZStack { 222 | Circle() 223 | .stroke(Color.progressBackgroundLinear, lineWidth: 24) 224 | .frame(width: 250, height: 250) 225 | 226 | Circle() 227 | .frame(width: 200, height: 200) 228 | 229 | ForEach(Array(stride(from: 0, through: 10, by: 1)), id: \.self) { i in 230 | Text("\(i * 10)") 231 | .rotationEffect(.degrees(-120 - Double(i * 30))) 232 | .offset(x: 160) 233 | .rotationEffect(.degrees(Double(i * 30))) 234 | } 235 | .rotationEffect(.degrees(120)) 236 | 237 | Circle() 238 | .trim(from: 0.1, to: speedSimulator.progress) 239 | .stroke(Color.progressLinear, style: StrokeStyle(lineWidth: 24, lineCap: .round)) 240 | .frame(width: 250, height: 250) 241 | .rotationEffect(.degrees(90)) 242 | } 243 | } 244 | } 245 | 246 | struct StartStopButtonView: View { 247 | @ObservedObject var speedSimulator: SpeedSimultor 248 | var body: some View { 249 | ZStack { 250 | RoundedRectangle(cornerRadius: 25) 251 | .fill(speedSimulator.start ? Color.stopColor : Color.darkPurple) 252 | .frame(width: 110, height: 50) 253 | 254 | HStack { 255 | Image(systemName: "power") 256 | .font(.system(size: 18, weight: .black)) 257 | Text(speedSimulator.start ? "Stop" : "Start") 258 | .font(.system(size: 18, weight: .regular)) 259 | } 260 | } 261 | } 262 | } 263 | 264 | struct DropdownView: View { 265 | @StateObject var dropdownManager = DropdownManager() 266 | var body: some View { 267 | ZStack { 268 | RoundedRectangle(cornerRadius: 20) 269 | .fill(Color.dropDown) 270 | .frame(height: dropdownManager.expanded ? 300 : 60) 271 | HStack(alignment: .top) { 272 | // drop down items 273 | if !dropdownManager.expanded { 274 | RegionItemView(region: dropdownManager.regions[dropdownManager.selectedIndex]) 275 | .onTapGesture { 276 | withAnimation { dropdownManager.expandCollapseView() } 277 | } 278 | } else { 279 | VStack(spacing: 0) { 280 | ForEach(dropdownManager.regions) { region in 281 | RegionItemView(region: region) 282 | .onTapGesture { 283 | withAnimation { dropdownManager.selectItem(region: region) } 284 | } 285 | } 286 | } 287 | } 288 | 289 | Image(systemName: "chevron.down") 290 | .font(.system(size: 18, weight: .bold)) 291 | .foregroundColor(.white) 292 | .rotationEffect(dropdownManager.expanded ? .degrees(180) : .zero) 293 | .padding() 294 | .padding(.top, 10) 295 | .onTapGesture { 296 | withAnimation { dropdownManager.expandCollapseView() } 297 | } 298 | } 299 | 300 | } 301 | } 302 | } 303 | 304 | struct RegionItemView: View { 305 | let region: Region 306 | var body: some View { 307 | ZStack { 308 | Rectangle() 309 | .fill(Color.white.opacity(0.001)) // so that whole item tapable 310 | .frame(height: 60) 311 | 312 | HStack(spacing: 16) { 313 | Text(region.imageName) 314 | .font(.system(size: 55)) 315 | .fixedSize() 316 | .frame(width: 30, height: 30) 317 | .cornerRadius(15) 318 | 319 | Text(region.name) 320 | .bold() 321 | .foregroundColor(.white) 322 | 323 | Spacer() 324 | 325 | ZStack(alignment: .leading) { 326 | HStack(spacing: 2) { 327 | ForEach(Array(stride(from: 0, to: 5, by: 1)), id: \.self) { _ in 328 | Rectangle() 329 | .fill(Color.gray) 330 | .frame(width: 6, height: 6) 331 | } 332 | } 333 | 334 | HStack(spacing: 2) { 335 | ForEach(Array(stride(from: 0, to: region.strength, by: 1)), id: \.self) { _ in 336 | Rectangle() 337 | .fill(Color.green) 338 | .frame(width: 6, height: 6) 339 | } 340 | } 341 | } 342 | } 343 | .padding(.horizontal) 344 | } 345 | } 346 | } 347 | 348 | struct SideMenuView: View { 349 | @State var startAnimation = false 350 | @Binding var showSideMenu: Bool 351 | var body: some View { 352 | ZStack { 353 | Rectangle() 354 | .fill(Color.black.opacity(0.1)) 355 | .ignoresSafeArea() // so other element behind side meue is not tappable 356 | // two rotated rectangle 357 | RoundedRectangle(cornerRadius: 40) 358 | .fill(Color.sideBackground) 359 | .rotationEffect(startAnimation ? .degrees(15) : .zero, anchor: .bottomTrailing) 360 | .offset(x: startAnimation ? -UIScreen.screenWidth/2 : -UIScreen.screenWidth, y: 20) 361 | .scaleEffect(0.85) 362 | 363 | RoundedRectangle(cornerRadius: 40) 364 | .fill(Color.sideBackground) 365 | .rotationEffect(startAnimation ? .degrees(10) : .zero, anchor: .bottomTrailing) 366 | .offset(x: startAnimation ? -UIScreen.screenWidth/2 : -UIScreen.screenWidth, y: 20) 367 | .scaleEffect(0.95) 368 | .shadow(color: .black, radius: 50) 369 | 370 | VStack(alignment: .leading) { 371 | Spacer() 372 | 373 | HStack { 374 | Spacer() 375 | 376 | Button(action: { 377 | withAnimation { 378 | startAnimation.toggle() 379 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { 380 | self.showSideMenu = false 381 | } 382 | } 383 | }, label: { 384 | Image(systemName: "xmark") 385 | .font(.system(size: 20, weight: .bold)) 386 | .padding() 387 | }) 388 | } 389 | .offset(x: -UIScreen.screenWidth/4) 390 | // user view 391 | UserView() 392 | 393 | Spacer() 394 | 395 | // menus list 396 | MenuListView() 397 | 398 | Spacer() 399 | 400 | // go premium view 401 | PremiumView(fillRect: false) 402 | 403 | Spacer(minLength: 180) 404 | } 405 | .padding(.horizontal) 406 | .offset(x: startAnimation ? 0.0 : -UIScreen.screenWidth) 407 | .animation(.easeIn(duration: 0.3)) 408 | } 409 | .onAppear { 410 | withAnimation { startAnimation.toggle() } 411 | } 412 | } 413 | } 414 | 415 | struct UserView: View { 416 | var body: some View { 417 | VStack(alignment: .leading) { 418 | RoundedRectangle(cornerRadius: 10) 419 | .frame(width: 70, height: 70) 420 | 421 | Text("Hello,") 422 | Text("Kavin Breadly") 423 | .bold() 424 | } 425 | } 426 | } 427 | 428 | struct MenuListView: View { 429 | var body: some View { 430 | ForEach(Data.menus) { menu in 431 | MenuItemView(menu: menu) 432 | .padding(.vertical, 8) 433 | } 434 | } 435 | } 436 | 437 | struct MenuItemView: View { 438 | let menu: MenuItem 439 | var body: some View { 440 | HStack(spacing: 14) { 441 | Image(systemName: menu.imageName) 442 | .fixedSize(horizontal: true, vertical: true) 443 | .frame(width: 20) 444 | Text(menu.name) 445 | .font(.system(size: 14, weight: .bold)) 446 | } 447 | } 448 | } 449 | 450 | struct MenuItem: Identifiable { 451 | let id = UUID() 452 | let name: String 453 | let imageName: String 454 | } 455 | 456 | struct Region: Identifiable { 457 | let id = UUID() 458 | let name: String 459 | let imageName: String 460 | let strength: Int 461 | } 462 | 463 | class DropdownManager: ObservableObject { 464 | @Published var regions = Data.regions 465 | @Published var expanded = false 466 | var selectedIndex = 0 467 | 468 | func expandCollapseView() { 469 | expanded.toggle() 470 | } 471 | 472 | func selectItem(region: Region) { 473 | if let index = regions.firstIndex(where: { $0.id == region.id }) { 474 | expandCollapseView() 475 | selectedIndex = index 476 | } 477 | } 478 | 479 | } 480 | 481 | class SpeedSimultor: ObservableObject { 482 | @Published var progress: CGFloat = 0.0 483 | @Published var start = false 484 | 485 | let expectedRange = 35...40 486 | var calculatedSpeed: CGFloat = 0.0 487 | 488 | func startSpeedTest() { 489 | start.toggle() 490 | 491 | for i in Array(stride(from: 0, through: 60, by: 0.2)) { 492 | 493 | DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)) { 494 | if self.start { 495 | self.calculateRandomSpeed() 496 | self.calculateProgress() 497 | } 498 | } 499 | } 500 | } 501 | 502 | func calculateRandomSpeed() { 503 | let speed = Float(arc4random() % 8) 504 | 505 | if expectedRange.contains(Int(calculatedSpeed)) { 506 | if speed > 4 { 507 | calculatedSpeed += CGFloat(speed/5) 508 | } else { 509 | calculatedSpeed -= CGFloat(speed/5) 510 | } 511 | } else if calculatedSpeed > 40 { 512 | calculatedSpeed -= CGFloat(speed/6) 513 | } else { 514 | calculatedSpeed += CGFloat(speed/2) 515 | } 516 | } 517 | 518 | func calculateProgress() { 519 | withAnimation(.linear(duration: 0.2)) { 520 | progress = calculatedSpeed/125 + 0.1 521 | } 522 | } 523 | } 524 | 525 | struct Data { 526 | static let regions = [ 527 | Region(name: "Singapore", imageName: "🇸🇬", strength: 4), 528 | Region(name: "USA", imageName: "🇺🇸", strength: 3), 529 | Region(name: "Australia", imageName: "🇦🇺", strength: 3), 530 | Region(name: "Canada", imageName: "🇨🇦", strength: 2), 531 | Region(name: "France", imageName: "🇫🇷", strength: 2) 532 | ] 533 | 534 | static let menus = [ 535 | MenuItem(name: "Apps using VPN", imageName: "icloud.and.arrow.down"), 536 | MenuItem(name: "Rate us", imageName: "star"), 537 | MenuItem(name: "Support", imageName: "questionmark.circle"), 538 | MenuItem(name: "Settings", imageName: "gearshape"), 539 | ] 540 | } 541 | 542 | extension Color { 543 | static let appPrimary = Color.init(red: 84/255, green: 31/255, blue: 221/255) 544 | static let dropDown = Color.init(red: 28/255, green: 24/255, blue: 197/255) 545 | static let progressBackground = Color.init(red: 149/255, green: 112/255, blue: 250/255) 546 | static let progress = Color.init(red: 252/255, green: 229/255, blue: 96/255) 547 | static let darkPurple = Color.init(red: 169/255, green: 41/255, blue: 246/255) 548 | static let viewTop = Color.init(red: 187/255, green: 68/255, blue: 251/255) 549 | static let viewBottom = Color.init(red: 104/255, green: 36/255, blue: 242/255) 550 | static let stopColor = Color.init(red: 250/255, green: 140/255, blue: 82/255) 551 | 552 | static let progressLinear = LinearGradient( 553 | gradient: Gradient(colors: 554 | [Color.progress, 555 | Color.progress.opacity(0.01)]), 556 | startPoint: .leading, 557 | endPoint: .trailing) 558 | static let progressBackgroundLinear = LinearGradient( 559 | gradient: Gradient(colors: 560 | [Color.progressBackground, 561 | Color.progressBackground.opacity(0.01)]), 562 | startPoint: .top, 563 | endPoint: .bottom) 564 | 565 | static let sideBackground = LinearGradient(gradient: Gradient(colors: [Color.viewTop, Color.viewBottom]), startPoint: .top, endPoint: .bottom) 566 | } 567 | 568 | extension UIScreen { 569 | static let screenWidth = UIScreen.main.bounds.size.width 570 | static let screenHeight = UIScreen.main.bounds.size.height 571 | static let screenSize = UIScreen.main.bounds.size 572 | } 573 | 574 | struct ContentView_Previews: PreviewProvider { 575 | static var previews: some View { 576 | ContentView() 577 | } 578 | } 579 | --------------------------------------------------------------------------------