├── README.md ├── TB_TwitterHeader.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── yari.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── yari.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── TB_TwitterHeader.xcscheme │ └── xcschememanagement.plist └── TB_TwitterHeader ├── AppDelegate.swift ├── AvatarImageView.swift ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── FXBlurView.h ├── FXBlurView.m ├── Images.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── header_bg.imageset │ ├── Contents.json │ └── header_bg@2x.jpg ├── love.imageset │ ├── Contents.json │ └── love@2x.png ├── profile.imageset │ ├── Contents.json │ └── profile@2x.jpg └── t&b.imageset │ ├── Contents.json │ └── t&b@2x.png ├── Info.plist ├── TB_TwitterHeader-Bridging-Header.h ├── TWTButton.swift └── ViewController.swift /README.md: -------------------------------------------------------------------------------- 1 | # TB_TwitterUI 2 | This is the code for the ThinkAndBuild tutorial: ["Implementing the Twitter App UI"](http://www.thinkandbuild.it/implementing-the-twitter-ios-app-ui/) 3 | 4 | -------------------------------------------------------------------------------- /TB_TwitterHeader.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 583C54FF1DFA15F80087D73C /* FXBlurView.m in Sources */ = {isa = PBXBuildFile; fileRef = 583C54FE1DFA15F80087D73C /* FXBlurView.m */; }; 11 | 58B112631DFA11DB000162BD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B112621DFA11DB000162BD /* AppDelegate.swift */; }; 12 | 58B112651DFA11DB000162BD /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B112641DFA11DB000162BD /* ViewController.swift */; }; 13 | 58B112681DFA11DB000162BD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 58B112661DFA11DB000162BD /* Main.storyboard */; }; 14 | 58B1126D1DFA11DB000162BD /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 58B1126B1DFA11DB000162BD /* LaunchScreen.storyboard */; }; 15 | 58B1127A1DFA1293000162BD /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 58B112791DFA1293000162BD /* Images.xcassets */; }; 16 | 58B1127C1DFA12C8000162BD /* TWTButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B1127B1DFA12C8000162BD /* TWTButton.swift */; }; 17 | 58B1127E1DFA131E000162BD /* AvatarImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B1127D1DFA131E000162BD /* AvatarImageView.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 583C54FD1DFA15F80087D73C /* FXBlurView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FXBlurView.h; sourceTree = ""; }; 22 | 583C54FE1DFA15F80087D73C /* FXBlurView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FXBlurView.m; sourceTree = ""; }; 23 | 58B1125F1DFA11DB000162BD /* TB_TwitterHeader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TB_TwitterHeader.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 58B112621DFA11DB000162BD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 25 | 58B112641DFA11DB000162BD /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 26 | 58B112671DFA11DB000162BD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 27 | 58B1126C1DFA11DB000162BD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 28 | 58B1126E1DFA11DB000162BD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 29 | 58B112751DFA1232000162BD /* TB_TwitterHeader-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TB_TwitterHeader-Bridging-Header.h"; sourceTree = ""; }; 30 | 58B112791DFA1293000162BD /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 31 | 58B1127B1DFA12C8000162BD /* TWTButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TWTButton.swift; sourceTree = ""; }; 32 | 58B1127D1DFA131E000162BD /* AvatarImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AvatarImageView.swift; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 58B1125C1DFA11DA000162BD /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 58B112561DFA11DA000162BD = { 47 | isa = PBXGroup; 48 | children = ( 49 | 58B112611DFA11DB000162BD /* TB_TwitterHeader */, 50 | 58B112601DFA11DB000162BD /* Products */, 51 | ); 52 | sourceTree = ""; 53 | }; 54 | 58B112601DFA11DB000162BD /* Products */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 58B1125F1DFA11DB000162BD /* TB_TwitterHeader.app */, 58 | ); 59 | name = Products; 60 | sourceTree = ""; 61 | }; 62 | 58B112611DFA11DB000162BD /* TB_TwitterHeader */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 58B112741DFA121E000162BD /* Helpers */, 66 | 58B112621DFA11DB000162BD /* AppDelegate.swift */, 67 | 58B112641DFA11DB000162BD /* ViewController.swift */, 68 | 58B112661DFA11DB000162BD /* Main.storyboard */, 69 | 58B112791DFA1293000162BD /* Images.xcassets */, 70 | 58B1126B1DFA11DB000162BD /* LaunchScreen.storyboard */, 71 | 58B1126E1DFA11DB000162BD /* Info.plist */, 72 | ); 73 | path = TB_TwitterHeader; 74 | sourceTree = ""; 75 | }; 76 | 58B112741DFA121E000162BD /* Helpers */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 583C54FD1DFA15F80087D73C /* FXBlurView.h */, 80 | 583C54FE1DFA15F80087D73C /* FXBlurView.m */, 81 | 58B1127D1DFA131E000162BD /* AvatarImageView.swift */, 82 | 58B1127B1DFA12C8000162BD /* TWTButton.swift */, 83 | 58B112751DFA1232000162BD /* TB_TwitterHeader-Bridging-Header.h */, 84 | ); 85 | name = Helpers; 86 | sourceTree = ""; 87 | }; 88 | /* End PBXGroup section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | 58B1125E1DFA11DA000162BD /* TB_TwitterHeader */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = 58B112711DFA11DB000162BD /* Build configuration list for PBXNativeTarget "TB_TwitterHeader" */; 94 | buildPhases = ( 95 | 58B1125B1DFA11DA000162BD /* Sources */, 96 | 58B1125C1DFA11DA000162BD /* Frameworks */, 97 | 58B1125D1DFA11DA000162BD /* Resources */, 98 | ); 99 | buildRules = ( 100 | ); 101 | dependencies = ( 102 | ); 103 | name = TB_TwitterHeader; 104 | productName = TB_TwitterHeader; 105 | productReference = 58B1125F1DFA11DB000162BD /* TB_TwitterHeader.app */; 106 | productType = "com.apple.product-type.application"; 107 | }; 108 | /* End PBXNativeTarget section */ 109 | 110 | /* Begin PBXProject section */ 111 | 58B112571DFA11DA000162BD /* Project object */ = { 112 | isa = PBXProject; 113 | attributes = { 114 | LastSwiftUpdateCheck = 0810; 115 | LastUpgradeCheck = 0810; 116 | ORGANIZATIONNAME = Bitwaker; 117 | TargetAttributes = { 118 | 58B1125E1DFA11DA000162BD = { 119 | CreatedOnToolsVersion = 8.1; 120 | DevelopmentTeam = T5V89Y6UVF; 121 | LastSwiftMigration = 0810; 122 | ProvisioningStyle = Automatic; 123 | }; 124 | }; 125 | }; 126 | buildConfigurationList = 58B1125A1DFA11DA000162BD /* Build configuration list for PBXProject "TB_TwitterHeader" */; 127 | compatibilityVersion = "Xcode 3.2"; 128 | developmentRegion = English; 129 | hasScannedForEncodings = 0; 130 | knownRegions = ( 131 | en, 132 | Base, 133 | ); 134 | mainGroup = 58B112561DFA11DA000162BD; 135 | productRefGroup = 58B112601DFA11DB000162BD /* Products */; 136 | projectDirPath = ""; 137 | projectRoot = ""; 138 | targets = ( 139 | 58B1125E1DFA11DA000162BD /* TB_TwitterHeader */, 140 | ); 141 | }; 142 | /* End PBXProject section */ 143 | 144 | /* Begin PBXResourcesBuildPhase section */ 145 | 58B1125D1DFA11DA000162BD /* Resources */ = { 146 | isa = PBXResourcesBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | 58B1126D1DFA11DB000162BD /* LaunchScreen.storyboard in Resources */, 150 | 58B1127A1DFA1293000162BD /* Images.xcassets in Resources */, 151 | 58B112681DFA11DB000162BD /* Main.storyboard in Resources */, 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | /* End PBXResourcesBuildPhase section */ 156 | 157 | /* Begin PBXSourcesBuildPhase section */ 158 | 58B1125B1DFA11DA000162BD /* Sources */ = { 159 | isa = PBXSourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 58B112651DFA11DB000162BD /* ViewController.swift in Sources */, 163 | 58B1127E1DFA131E000162BD /* AvatarImageView.swift in Sources */, 164 | 583C54FF1DFA15F80087D73C /* FXBlurView.m in Sources */, 165 | 58B1127C1DFA12C8000162BD /* TWTButton.swift in Sources */, 166 | 58B112631DFA11DB000162BD /* AppDelegate.swift in Sources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXSourcesBuildPhase section */ 171 | 172 | /* Begin PBXVariantGroup section */ 173 | 58B112661DFA11DB000162BD /* Main.storyboard */ = { 174 | isa = PBXVariantGroup; 175 | children = ( 176 | 58B112671DFA11DB000162BD /* Base */, 177 | ); 178 | name = Main.storyboard; 179 | sourceTree = ""; 180 | }; 181 | 58B1126B1DFA11DB000162BD /* LaunchScreen.storyboard */ = { 182 | isa = PBXVariantGroup; 183 | children = ( 184 | 58B1126C1DFA11DB000162BD /* Base */, 185 | ); 186 | name = LaunchScreen.storyboard; 187 | sourceTree = ""; 188 | }; 189 | /* End PBXVariantGroup section */ 190 | 191 | /* Begin XCBuildConfiguration section */ 192 | 58B1126F1DFA11DB000162BD /* Debug */ = { 193 | isa = XCBuildConfiguration; 194 | buildSettings = { 195 | ALWAYS_SEARCH_USER_PATHS = NO; 196 | CLANG_ANALYZER_NONNULL = YES; 197 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 198 | CLANG_CXX_LIBRARY = "libc++"; 199 | CLANG_ENABLE_MODULES = YES; 200 | CLANG_ENABLE_OBJC_ARC = YES; 201 | CLANG_WARN_BOOL_CONVERSION = YES; 202 | CLANG_WARN_CONSTANT_CONVERSION = YES; 203 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 204 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 205 | CLANG_WARN_EMPTY_BODY = YES; 206 | CLANG_WARN_ENUM_CONVERSION = YES; 207 | CLANG_WARN_INFINITE_RECURSION = YES; 208 | CLANG_WARN_INT_CONVERSION = YES; 209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 210 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 211 | CLANG_WARN_UNREACHABLE_CODE = YES; 212 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 213 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 214 | COPY_PHASE_STRIP = NO; 215 | DEBUG_INFORMATION_FORMAT = dwarf; 216 | ENABLE_STRICT_OBJC_MSGSEND = YES; 217 | ENABLE_TESTABILITY = YES; 218 | GCC_C_LANGUAGE_STANDARD = gnu99; 219 | GCC_DYNAMIC_NO_PIC = NO; 220 | GCC_NO_COMMON_BLOCKS = YES; 221 | GCC_OPTIMIZATION_LEVEL = 0; 222 | GCC_PREPROCESSOR_DEFINITIONS = ( 223 | "DEBUG=1", 224 | "$(inherited)", 225 | ); 226 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 227 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 228 | GCC_WARN_UNDECLARED_SELECTOR = YES; 229 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 230 | GCC_WARN_UNUSED_FUNCTION = YES; 231 | GCC_WARN_UNUSED_VARIABLE = YES; 232 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 233 | MTL_ENABLE_DEBUG_INFO = YES; 234 | ONLY_ACTIVE_ARCH = YES; 235 | SDKROOT = iphoneos; 236 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 237 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 238 | TARGETED_DEVICE_FAMILY = "1,2"; 239 | }; 240 | name = Debug; 241 | }; 242 | 58B112701DFA11DB000162BD /* Release */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BOOL_CONVERSION = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 254 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 255 | CLANG_WARN_EMPTY_BODY = YES; 256 | CLANG_WARN_ENUM_CONVERSION = YES; 257 | CLANG_WARN_INFINITE_RECURSION = YES; 258 | CLANG_WARN_INT_CONVERSION = YES; 259 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 260 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 261 | CLANG_WARN_UNREACHABLE_CODE = YES; 262 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 263 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 264 | COPY_PHASE_STRIP = NO; 265 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 266 | ENABLE_NS_ASSERTIONS = NO; 267 | ENABLE_STRICT_OBJC_MSGSEND = YES; 268 | GCC_C_LANGUAGE_STANDARD = gnu99; 269 | GCC_NO_COMMON_BLOCKS = YES; 270 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 271 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 272 | GCC_WARN_UNDECLARED_SELECTOR = YES; 273 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 274 | GCC_WARN_UNUSED_FUNCTION = YES; 275 | GCC_WARN_UNUSED_VARIABLE = YES; 276 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 277 | MTL_ENABLE_DEBUG_INFO = NO; 278 | SDKROOT = iphoneos; 279 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 280 | TARGETED_DEVICE_FAMILY = "1,2"; 281 | VALIDATE_PRODUCT = YES; 282 | }; 283 | name = Release; 284 | }; 285 | 58B112721DFA11DB000162BD /* Debug */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | DEVELOPMENT_TEAM = T5V89Y6UVF; 291 | INFOPLIST_FILE = TB_TwitterHeader/Info.plist; 292 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 293 | PRODUCT_BUNDLE_IDENTIFIER = "com.bitwaker.TB-TwitterHeader"; 294 | PRODUCT_NAME = "$(TARGET_NAME)"; 295 | SWIFT_OBJC_BRIDGING_HEADER = "TB_TwitterHeader/TB_TwitterHeader-Bridging-Header.h"; 296 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 297 | SWIFT_VERSION = 3.0; 298 | }; 299 | name = Debug; 300 | }; 301 | 58B112731DFA11DB000162BD /* Release */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 305 | CLANG_ENABLE_MODULES = YES; 306 | DEVELOPMENT_TEAM = T5V89Y6UVF; 307 | INFOPLIST_FILE = TB_TwitterHeader/Info.plist; 308 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 309 | PRODUCT_BUNDLE_IDENTIFIER = "com.bitwaker.TB-TwitterHeader"; 310 | PRODUCT_NAME = "$(TARGET_NAME)"; 311 | SWIFT_OBJC_BRIDGING_HEADER = "TB_TwitterHeader/TB_TwitterHeader-Bridging-Header.h"; 312 | SWIFT_VERSION = 3.0; 313 | }; 314 | name = Release; 315 | }; 316 | /* End XCBuildConfiguration section */ 317 | 318 | /* Begin XCConfigurationList section */ 319 | 58B1125A1DFA11DA000162BD /* Build configuration list for PBXProject "TB_TwitterHeader" */ = { 320 | isa = XCConfigurationList; 321 | buildConfigurations = ( 322 | 58B1126F1DFA11DB000162BD /* Debug */, 323 | 58B112701DFA11DB000162BD /* Release */, 324 | ); 325 | defaultConfigurationIsVisible = 0; 326 | defaultConfigurationName = Release; 327 | }; 328 | 58B112711DFA11DB000162BD /* Build configuration list for PBXNativeTarget "TB_TwitterHeader" */ = { 329 | isa = XCConfigurationList; 330 | buildConfigurations = ( 331 | 58B112721DFA11DB000162BD /* Debug */, 332 | 58B112731DFA11DB000162BD /* Release */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | /* End XCConfigurationList section */ 338 | }; 339 | rootObject = 58B112571DFA11DA000162BD /* Project object */; 340 | } 341 | -------------------------------------------------------------------------------- /TB_TwitterHeader.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TB_TwitterHeader.xcodeproj/project.xcworkspace/xcuserdata/yari.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ariok/TB_TwitterUI/82240c8a7b282e39e61015b079fc4b9b9a8472d6/TB_TwitterHeader.xcodeproj/project.xcworkspace/xcuserdata/yari.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TB_TwitterHeader.xcodeproj/xcuserdata/yari.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TB_TwitterHeader.xcodeproj/xcuserdata/yari.xcuserdatad/xcschemes/TB_TwitterHeader.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 | -------------------------------------------------------------------------------- /TB_TwitterHeader.xcodeproj/xcuserdata/yari.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TB_TwitterHeader.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 58B1125E1DFA11DA000162BD 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /TB_TwitterHeader/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // TB_TwitterHeader 4 | // 5 | // Created by Yari D'areglia on 08/12/2016. 6 | // 7 | 8 | import UIKit 9 | 10 | @UIApplicationMain 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | var window: UIWindow? 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // 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. 23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // 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. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // 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. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /TB_TwitterHeader/AvatarImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AvatarImageView.swift 3 | // AvatarImageView 4 | // 5 | // Created by Yari D'areglia on 08/12/2016. 6 | 7 | import UIKit 8 | 9 | class AvatarImageView: UIImageView { 10 | 11 | override func awakeFromNib() { 12 | self.layer.cornerRadius = 10.0 13 | self.layer.borderColor = UIColor.white.cgColor 14 | self.layer.borderWidth = 3.0 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TB_TwitterHeader/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 | -------------------------------------------------------------------------------- /TB_TwitterHeader/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 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 55 | 69 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /TB_TwitterHeader/FXBlurView.h: -------------------------------------------------------------------------------- 1 | // 2 | // FXBlurView.h 3 | // 4 | // Version 1.6.3 5 | // 6 | // Created by Nick Lockwood on 25/08/2013. 7 | // Copyright (c) 2013 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/FXBlurView 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | 34 | #import 35 | #import 36 | #import 37 | 38 | 39 | #pragma GCC diagnostic push 40 | #pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis" 41 | 42 | 43 | #import 44 | #undef weak_ref 45 | #if __has_feature(objc_arc) && __has_feature(objc_arc_weak) 46 | #define weak_ref weak 47 | #else 48 | #define weak_ref unsafe_unretained 49 | #endif 50 | 51 | @interface UIImage (FXBlurView) 52 | 53 | - (UIImage *)blurredImageWithRadius:(CGFloat)radius iterations:(NSUInteger)iterations tintColor:(UIColor *)tintColor; 54 | 55 | @end 56 | 57 | 58 | @interface FXBlurView : UIView 59 | 60 | + (void)setBlurEnabled:(BOOL)blurEnabled; 61 | + (void)setUpdatesEnabled; 62 | + (void)setUpdatesDisabled; 63 | 64 | @property (nonatomic, getter = isBlurEnabled) BOOL blurEnabled; 65 | @property (nonatomic, getter = isDynamic) BOOL dynamic; 66 | @property (nonatomic, assign) NSUInteger iterations; 67 | @property (nonatomic, assign) NSTimeInterval updateInterval; 68 | @property (nonatomic, assign) CGFloat blurRadius; 69 | @property (nonatomic, strong) UIColor *tintColor; 70 | @property (nonatomic, weak_ref) IBOutlet UIView *underlyingView; 71 | 72 | - (void)updateAsynchronously:(BOOL)async completion:(void (^)())completion; 73 | 74 | @end 75 | 76 | 77 | #pragma GCC diagnostic pop 78 | 79 | -------------------------------------------------------------------------------- /TB_TwitterHeader/FXBlurView.m: -------------------------------------------------------------------------------- 1 | // 2 | // FXBlurView.m 3 | // 4 | // Version 1.6.3 5 | // 6 | // Created by Nick Lockwood on 25/08/2013. 7 | // Copyright (c) 2013 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/FXBlurView 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | 34 | #import "FXBlurView.h" 35 | #import 36 | 37 | 38 | #pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis" 39 | #pragma GCC diagnostic ignored "-Wdirect-ivar-access" 40 | #pragma GCC diagnostic ignored "-Wgnu" 41 | 42 | 43 | #import 44 | #if !__has_feature(objc_arc) 45 | #error This class requires automatic reference counting 46 | #endif 47 | 48 | @implementation UIImage (FXBlurView) 49 | 50 | - (UIImage *)blurredImageWithRadius:(CGFloat)radius iterations:(NSUInteger)iterations tintColor:(UIColor *)tintColor 51 | { 52 | //image must be nonzero size 53 | if (floorf(self.size.width) * floorf(self.size.height) <= 0.0f) return self; 54 | 55 | //boxsize must be an odd integer 56 | uint32_t boxSize = (uint32_t)(radius * self.scale); 57 | if (boxSize % 2 == 0) boxSize ++; 58 | 59 | //create image buffers 60 | CGImageRef imageRef = self.CGImage; 61 | vImage_Buffer buffer1, buffer2; 62 | buffer1.width = buffer2.width = CGImageGetWidth(imageRef); 63 | buffer1.height = buffer2.height = CGImageGetHeight(imageRef); 64 | buffer1.rowBytes = buffer2.rowBytes = CGImageGetBytesPerRow(imageRef); 65 | size_t bytes = buffer1.rowBytes * buffer1.height; 66 | buffer1.data = malloc(bytes); 67 | buffer2.data = malloc(bytes); 68 | 69 | //create temp buffer 70 | void *tempBuffer = malloc((size_t)vImageBoxConvolve_ARGB8888(&buffer1, &buffer2, NULL, 0, 0, boxSize, boxSize, 71 | NULL, kvImageEdgeExtend + kvImageGetTempBufferSize)); 72 | 73 | //copy image data 74 | CFDataRef dataSource = CGDataProviderCopyData(CGImageGetDataProvider(imageRef)); 75 | memcpy(buffer1.data, CFDataGetBytePtr(dataSource), bytes); 76 | CFRelease(dataSource); 77 | 78 | for (NSUInteger i = 0; i < iterations; i++) 79 | { 80 | //perform blur 81 | vImageBoxConvolve_ARGB8888(&buffer1, &buffer2, tempBuffer, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend); 82 | 83 | //swap buffers 84 | void *temp = buffer1.data; 85 | buffer1.data = buffer2.data; 86 | buffer2.data = temp; 87 | } 88 | 89 | //free buffers 90 | free(buffer2.data); 91 | free(tempBuffer); 92 | 93 | //create image context from buffer 94 | CGContextRef ctx = CGBitmapContextCreate(buffer1.data, buffer1.width, buffer1.height, 95 | 8, buffer1.rowBytes, CGImageGetColorSpace(imageRef), 96 | CGImageGetBitmapInfo(imageRef)); 97 | 98 | //apply tint 99 | if (tintColor && CGColorGetAlpha(tintColor.CGColor) > 0.0f) 100 | { 101 | CGContextSetFillColorWithColor(ctx, [tintColor colorWithAlphaComponent:0.25].CGColor); 102 | CGContextSetBlendMode(ctx, kCGBlendModePlusLighter); 103 | CGContextFillRect(ctx, CGRectMake(0, 0, buffer1.width, buffer1.height)); 104 | } 105 | 106 | //create image from context 107 | imageRef = CGBitmapContextCreateImage(ctx); 108 | UIImage *image = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation]; 109 | CGImageRelease(imageRef); 110 | CGContextRelease(ctx); 111 | free(buffer1.data); 112 | return image; 113 | } 114 | 115 | @end 116 | 117 | 118 | @interface FXBlurScheduler : NSObject 119 | 120 | @property (nonatomic, strong) NSMutableArray *views; 121 | @property (nonatomic, assign) NSUInteger viewIndex; 122 | @property (nonatomic, assign) NSUInteger updatesEnabled; 123 | @property (nonatomic, assign) BOOL blurEnabled; 124 | @property (nonatomic, assign) BOOL updating; 125 | 126 | @end 127 | 128 | 129 | @interface FXBlurLayer: CALayer 130 | 131 | @property (nonatomic, assign) CGFloat blurRadius; 132 | 133 | @end 134 | 135 | 136 | @implementation FXBlurLayer 137 | 138 | @dynamic blurRadius; 139 | 140 | + (BOOL)needsDisplayForKey:(NSString *)key 141 | { 142 | if ([@[@"blurRadius", @"bounds", @"position"] containsObject:key]) 143 | { 144 | return YES; 145 | } 146 | return [super needsDisplayForKey:key]; 147 | } 148 | 149 | @end 150 | 151 | 152 | @interface FXBlurView () 153 | 154 | @property (nonatomic, assign) BOOL iterationsSet; 155 | @property (nonatomic, assign) BOOL blurRadiusSet; 156 | @property (nonatomic, assign) BOOL dynamicSet; 157 | @property (nonatomic, assign) BOOL blurEnabledSet; 158 | @property (nonatomic, strong) NSDate *lastUpdate; 159 | 160 | - (UIImage *)snapshotOfUnderlyingView; 161 | - (BOOL)shouldUpdate; 162 | 163 | @end 164 | 165 | 166 | @implementation FXBlurScheduler 167 | 168 | + (instancetype)sharedInstance 169 | { 170 | static FXBlurScheduler *sharedInstance = nil; 171 | if (!sharedInstance) 172 | { 173 | sharedInstance = [[FXBlurScheduler alloc] init]; 174 | } 175 | return sharedInstance; 176 | } 177 | 178 | - (instancetype)init 179 | { 180 | if ((self = [super init])) 181 | { 182 | _updatesEnabled = 1; 183 | _blurEnabled = YES; 184 | _views = [[NSMutableArray alloc] init]; 185 | } 186 | return self; 187 | } 188 | 189 | - (void)setBlurEnabled:(BOOL)blurEnabled 190 | { 191 | _blurEnabled = blurEnabled; 192 | if (blurEnabled) 193 | { 194 | for (FXBlurView *view in self.views) 195 | { 196 | [view setNeedsDisplay]; 197 | } 198 | [self updateAsynchronously]; 199 | } 200 | } 201 | 202 | - (void)setUpdatesEnabled 203 | { 204 | _updatesEnabled ++; 205 | [self updateAsynchronously]; 206 | } 207 | 208 | - (void)setUpdatesDisabled 209 | { 210 | _updatesEnabled --; 211 | } 212 | 213 | - (void)addView:(FXBlurView *)view 214 | { 215 | if (![self.views containsObject:view]) 216 | { 217 | [self.views addObject:view]; 218 | [self updateAsynchronously]; 219 | } 220 | } 221 | 222 | - (void)removeView:(FXBlurView *)view 223 | { 224 | NSUInteger index = [self.views indexOfObject:view]; 225 | if (index != NSNotFound) 226 | { 227 | if (index <= self.viewIndex) 228 | { 229 | self.viewIndex --; 230 | } 231 | [self.views removeObjectAtIndex:index]; 232 | } 233 | } 234 | 235 | - (void)updateAsynchronously 236 | { 237 | if (self.blurEnabled && !self.updating && self.updatesEnabled > 0 && [self.views count]) 238 | { 239 | NSTimeInterval timeUntilNextUpdate = 1.0 / 60; 240 | 241 | //loop through until we find a view that's ready to be drawn 242 | self.viewIndex = self.viewIndex % [self.views count]; 243 | for (NSUInteger i = self.viewIndex; i < [self.views count]; i++) 244 | { 245 | FXBlurView *view = self.views[i]; 246 | if (view.dynamic && !view.hidden && view.window && [view shouldUpdate]) 247 | { 248 | NSTimeInterval nextUpdate = [view.lastUpdate timeIntervalSinceNow] + view.updateInterval; 249 | if (!view.lastUpdate || nextUpdate <= 0) 250 | { 251 | self.updating = YES; 252 | [view updateAsynchronously:YES completion:^{ 253 | 254 | //render next view 255 | self.updating = NO; 256 | self.viewIndex = i + 1; 257 | [self updateAsynchronously]; 258 | }]; 259 | return; 260 | } 261 | else 262 | { 263 | timeUntilNextUpdate = MIN(timeUntilNextUpdate, nextUpdate); 264 | } 265 | } 266 | } 267 | 268 | //try again, delaying until the time when the next view needs an update. 269 | self.viewIndex = 0; 270 | [self performSelector:@selector(updateAsynchronously) 271 | withObject:nil 272 | afterDelay:timeUntilNextUpdate 273 | inModes:@[NSDefaultRunLoopMode, UITrackingRunLoopMode]]; 274 | } 275 | } 276 | 277 | @end 278 | 279 | 280 | @implementation FXBlurView 281 | 282 | + (void)setBlurEnabled:(BOOL)blurEnabled 283 | { 284 | [FXBlurScheduler sharedInstance].blurEnabled = blurEnabled; 285 | } 286 | 287 | + (void)setUpdatesEnabled 288 | { 289 | [[FXBlurScheduler sharedInstance] setUpdatesEnabled]; 290 | } 291 | 292 | + (void)setUpdatesDisabled 293 | { 294 | [[FXBlurScheduler sharedInstance] setUpdatesDisabled]; 295 | } 296 | 297 | + (Class)layerClass 298 | { 299 | return [FXBlurLayer class]; 300 | } 301 | 302 | - (void)setUp 303 | { 304 | if (!_iterationsSet) _iterations = 3; 305 | if (!_blurRadiusSet) [self blurLayer].blurRadius = 40; 306 | if (!_dynamicSet) _dynamic = YES; 307 | if (!_blurEnabledSet) _blurEnabled = YES; 308 | self.updateInterval = _updateInterval; 309 | self.layer.magnificationFilter = @"linear"; // kCAFilterLinear 310 | 311 | unsigned int numberOfMethods; 312 | Method *methods = class_copyMethodList([UIView class], &numberOfMethods); 313 | for (unsigned int i = 0; i < numberOfMethods; i++) 314 | { 315 | Method method = methods[i]; 316 | SEL selector = method_getName(method); 317 | if (selector == @selector(tintColor)) 318 | { 319 | _tintColor = ((id (*)(id,SEL))method_getImplementation(method))(self, selector); 320 | break; 321 | } 322 | } 323 | free(methods); 324 | } 325 | 326 | - (id)initWithFrame:(CGRect)frame 327 | { 328 | if ((self = [super initWithFrame:frame])) 329 | { 330 | [self setUp]; 331 | self.clipsToBounds = YES; 332 | } 333 | return self; 334 | } 335 | 336 | - (id)initWithCoder:(NSCoder *)aDecoder 337 | { 338 | if ((self = [super initWithCoder:aDecoder])) 339 | { 340 | [self setUp]; 341 | } 342 | return self; 343 | } 344 | 345 | - (void)dealloc 346 | { 347 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 348 | } 349 | 350 | - (void)setIterations:(NSUInteger)iterations 351 | { 352 | _iterationsSet = YES; 353 | _iterations = iterations; 354 | [self setNeedsDisplay]; 355 | } 356 | 357 | - (void)setBlurRadius:(CGFloat)blurRadius 358 | { 359 | _blurRadiusSet = YES; 360 | [self blurLayer].blurRadius = blurRadius; 361 | } 362 | 363 | - (CGFloat)blurRadius 364 | { 365 | return [self blurLayer].blurRadius; 366 | } 367 | 368 | - (void)setBlurEnabled:(BOOL)blurEnabled 369 | { 370 | _blurEnabledSet = YES; 371 | if (_blurEnabled != blurEnabled) 372 | { 373 | _blurEnabled = blurEnabled; 374 | [self schedule]; 375 | if (_blurEnabled) 376 | { 377 | [self setNeedsDisplay]; 378 | } 379 | } 380 | } 381 | 382 | - (void)setDynamic:(BOOL)dynamic 383 | { 384 | _dynamicSet = YES; 385 | if (_dynamic != dynamic) 386 | { 387 | _dynamic = dynamic; 388 | [self schedule]; 389 | if (!dynamic) 390 | { 391 | [self setNeedsDisplay]; 392 | } 393 | } 394 | } 395 | 396 | - (UIView *)underlyingView 397 | { 398 | return _underlyingView ?: self.superview; 399 | } 400 | 401 | - (CALayer *)underlyingLayer 402 | { 403 | return self.underlyingView.layer; 404 | } 405 | 406 | - (FXBlurLayer *)blurLayer 407 | { 408 | return (FXBlurLayer *)self.layer; 409 | } 410 | 411 | - (FXBlurLayer *)blurPresentationLayer 412 | { 413 | FXBlurLayer *blurLayer = [self blurLayer]; 414 | return (FXBlurLayer *)blurLayer.presentationLayer ?: blurLayer; 415 | } 416 | 417 | - (void)setUpdateInterval:(NSTimeInterval)updateInterval 418 | { 419 | _updateInterval = updateInterval; 420 | if (_updateInterval <= 0) _updateInterval = 1.0/60; 421 | } 422 | 423 | - (void)setTintColor:(UIColor *)tintColor 424 | { 425 | _tintColor = tintColor; 426 | [self setNeedsDisplay]; 427 | } 428 | 429 | - (void)didMoveToSuperview 430 | { 431 | [super didMoveToSuperview]; 432 | [self.layer setNeedsDisplay]; 433 | } 434 | 435 | - (void)didMoveToWindow 436 | { 437 | [super didMoveToWindow]; 438 | [self schedule]; 439 | } 440 | 441 | - (void)schedule 442 | { 443 | if (self.window && self.dynamic && self.blurEnabled) 444 | { 445 | [[FXBlurScheduler sharedInstance] addView:self]; 446 | } 447 | else 448 | { 449 | [[FXBlurScheduler sharedInstance] removeView:self]; 450 | } 451 | } 452 | 453 | - (void)setNeedsDisplay 454 | { 455 | [super setNeedsDisplay]; 456 | [self.layer setNeedsDisplay]; 457 | } 458 | 459 | - (BOOL)shouldUpdate 460 | { 461 | __strong CALayer *underlyingLayer = [self underlyingLayer]; 462 | 463 | return 464 | underlyingLayer && !underlyingLayer.hidden && 465 | self.blurEnabled && [FXBlurScheduler sharedInstance].blurEnabled && 466 | !CGRectIsEmpty([self.layer.presentationLayer ?: self.layer bounds]) && !CGRectIsEmpty(underlyingLayer.bounds); 467 | } 468 | 469 | - (void)displayLayer:(__unused CALayer *)layer 470 | { 471 | [self updateAsynchronously:NO completion:NULL]; 472 | } 473 | 474 | - (id)actionForLayer:(CALayer *)layer forKey:(NSString *)key 475 | { 476 | if ([key isEqualToString:@"blurRadius"]) 477 | { 478 | //animations are enabled 479 | CAAnimation *action = (CAAnimation *)[super actionForLayer:layer forKey:@"backgroundColor"]; 480 | if ((NSNull *)action != [NSNull null]) 481 | { 482 | CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key]; 483 | animation.fromValue = [layer.presentationLayer valueForKey:key]; 484 | 485 | //CAMediatiming attributes 486 | animation.beginTime = action.beginTime; 487 | animation.duration = action.duration; 488 | animation.speed = action.speed; 489 | animation.timeOffset = action.timeOffset; 490 | animation.repeatCount = action.repeatCount; 491 | animation.repeatDuration = action.repeatDuration; 492 | animation.autoreverses = action.autoreverses; 493 | animation.fillMode = action.fillMode; 494 | 495 | //CAAnimation attributes 496 | animation.timingFunction = action.timingFunction; 497 | animation.delegate = action.delegate; 498 | 499 | return animation; 500 | } 501 | } 502 | return [super actionForLayer:layer forKey:key]; 503 | } 504 | 505 | - (UIImage *)snapshotOfUnderlyingView 506 | { 507 | __strong FXBlurLayer *blurLayer = [self blurPresentationLayer]; 508 | __strong CALayer *underlyingLayer = [self underlyingLayer]; 509 | CGRect bounds = [blurLayer convertRect:blurLayer.bounds toLayer:underlyingLayer]; 510 | 511 | self.lastUpdate = [NSDate date]; 512 | CGFloat scale = 0.5; 513 | if (self.iterations) 514 | { 515 | CGFloat blockSize = 12.0f/self.iterations; 516 | scale = blockSize/MAX(blockSize * 2, blurLayer.blurRadius); 517 | scale = 1.0f/floorf(1.0f/scale); 518 | } 519 | CGSize size = bounds.size; 520 | if (self.contentMode == UIViewContentModeScaleToFill || 521 | self.contentMode == UIViewContentModeScaleAspectFill || 522 | self.contentMode == UIViewContentModeScaleAspectFit || 523 | self.contentMode == UIViewContentModeRedraw) 524 | { 525 | //prevents edge artefacts 526 | size.width = floorf(size.width * scale) / scale; 527 | size.height = floorf(size.height * scale) / scale; 528 | } 529 | else if ([[UIDevice currentDevice].systemVersion floatValue] < 7.0f && [UIScreen mainScreen].scale == 1.0f) 530 | { 531 | //prevents pixelation on old devices 532 | scale = 1.0f; 533 | } 534 | UIGraphicsBeginImageContextWithOptions(size, NO, scale); 535 | CGContextRef context = UIGraphicsGetCurrentContext(); 536 | CGContextTranslateCTM(context, -bounds.origin.x, -bounds.origin.y); 537 | 538 | NSArray *hiddenViews = [self prepareUnderlyingViewForSnapshot]; 539 | [underlyingLayer renderInContext:context]; 540 | [self restoreSuperviewAfterSnapshot:hiddenViews]; 541 | UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); 542 | UIGraphicsEndImageContext(); 543 | return snapshot; 544 | } 545 | 546 | - (NSArray *)prepareUnderlyingViewForSnapshot 547 | { 548 | __strong CALayer *blurlayer = [self blurLayer]; 549 | __strong CALayer *underlyingLayer = [self underlyingLayer]; 550 | while (blurlayer.superlayer && blurlayer.superlayer != underlyingLayer) 551 | { 552 | blurlayer = blurlayer.superlayer; 553 | } 554 | NSMutableArray *layers = [NSMutableArray array]; 555 | NSUInteger index = [underlyingLayer.sublayers indexOfObject:blurlayer]; 556 | if (index != NSNotFound) 557 | { 558 | for (NSUInteger i = index; i < [underlyingLayer.sublayers count]; i++) 559 | { 560 | CALayer *layer = underlyingLayer.sublayers[i]; 561 | if (!layer.hidden) 562 | { 563 | layer.hidden = YES; 564 | [layers addObject:layer]; 565 | } 566 | } 567 | } 568 | return layers; 569 | } 570 | 571 | - (void)restoreSuperviewAfterSnapshot:(NSArray *)hiddenLayers 572 | { 573 | for (CALayer *layer in hiddenLayers) 574 | { 575 | layer.hidden = NO; 576 | } 577 | } 578 | 579 | - (UIImage *)blurredSnapshot:(UIImage *)snapshot radius:(CGFloat)blurRadius 580 | { 581 | return [snapshot blurredImageWithRadius:blurRadius 582 | iterations:self.iterations 583 | tintColor:self.tintColor]; 584 | } 585 | 586 | - (void)setLayerContents:(UIImage *)image 587 | { 588 | self.layer.contents = (id)image.CGImage; 589 | self.layer.contentsScale = image.scale; 590 | } 591 | 592 | - (void)updateAsynchronously:(BOOL)async completion:(void (^)())completion 593 | { 594 | if ([self shouldUpdate]) 595 | { 596 | UIImage *snapshot = [self snapshotOfUnderlyingView]; 597 | if (async) 598 | { 599 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 600 | 601 | UIImage *blurredImage = [self blurredSnapshot:snapshot radius:self.blurRadius]; 602 | dispatch_sync(dispatch_get_main_queue(), ^{ 603 | 604 | [self setLayerContents:blurredImage]; 605 | if (completion) completion(); 606 | }); 607 | }); 608 | } 609 | else 610 | { 611 | [self setLayerContents:[self blurredSnapshot:snapshot radius:[self blurPresentationLayer].blurRadius]]; 612 | if (completion) completion(); 613 | } 614 | } 615 | else if (completion) 616 | { 617 | completion(); 618 | } 619 | } 620 | 621 | @end 622 | -------------------------------------------------------------------------------- /TB_TwitterHeader/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /TB_TwitterHeader/Images.xcassets/header_bg.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "header_bg@2x.jpg" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TB_TwitterHeader/Images.xcassets/header_bg.imageset/header_bg@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ariok/TB_TwitterUI/82240c8a7b282e39e61015b079fc4b9b9a8472d6/TB_TwitterHeader/Images.xcassets/header_bg.imageset/header_bg@2x.jpg -------------------------------------------------------------------------------- /TB_TwitterHeader/Images.xcassets/love.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "love@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TB_TwitterHeader/Images.xcassets/love.imageset/love@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ariok/TB_TwitterUI/82240c8a7b282e39e61015b079fc4b9b9a8472d6/TB_TwitterHeader/Images.xcassets/love.imageset/love@2x.png -------------------------------------------------------------------------------- /TB_TwitterHeader/Images.xcassets/profile.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "profile@2x.jpg" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TB_TwitterHeader/Images.xcassets/profile.imageset/profile@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ariok/TB_TwitterUI/82240c8a7b282e39e61015b079fc4b9b9a8472d6/TB_TwitterHeader/Images.xcassets/profile.imageset/profile@2x.jpg -------------------------------------------------------------------------------- /TB_TwitterHeader/Images.xcassets/t&b.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "t&b@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /TB_TwitterHeader/Images.xcassets/t&b.imageset/t&b@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ariok/TB_TwitterUI/82240c8a7b282e39e61015b079fc4b9b9a8472d6/TB_TwitterHeader/Images.xcassets/t&b.imageset/t&b@2x.png -------------------------------------------------------------------------------- /TB_TwitterHeader/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 | 3.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /TB_TwitterHeader/TB_TwitterHeader-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "FXBlurView.h" 2 | -------------------------------------------------------------------------------- /TB_TwitterHeader/TWTButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TWTButton.swift 3 | // TB_TwitterHeader 4 | // 5 | // Created by Yari D'areglia on 08/12/2016. 6 | 7 | import UIKit 8 | 9 | class TWTButton: UIButton { 10 | 11 | override func awakeFromNib() { 12 | 13 | self.layer.cornerRadius = 5.0 14 | self.layer.borderWidth = 1.0 15 | self.layer.borderColor = UIColor(red: 85.0/255.0, green: 172.0/255.0, blue: 238.0/255.0, alpha: 1.0).cgColor 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /TB_TwitterHeader/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // TB_TwitterHeader 4 | // 5 | // Created by Yari D'areglia on 08/12/2016. 6 | 7 | import UIKit 8 | 9 | let offset_HeaderStop:CGFloat = 40.0 // At this offset the Header stops its transformations 10 | let offset_B_LabelHeader:CGFloat = 95.0 // At this offset the Black label reaches the Header 11 | let distance_W_LabelHeader:CGFloat = 35.0 // The distance between the bottom of the Header and the top of the White Label 12 | 13 | class ViewController: UIViewController, UIScrollViewDelegate { 14 | 15 | @IBOutlet var scrollView:UIScrollView! 16 | @IBOutlet var avatarImage:UIImageView! 17 | @IBOutlet var header:UIView! 18 | @IBOutlet var headerLabel:UILabel! 19 | @IBOutlet var headerImageView:UIImageView! 20 | @IBOutlet var headerBlurImageView:UIImageView! 21 | var blurredHeaderImageView:UIImageView? 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | scrollView.delegate = self 26 | } 27 | 28 | override func viewDidAppear(_ animated: Bool) { 29 | 30 | // Header - Image 31 | 32 | headerImageView = UIImageView(frame: header.bounds) 33 | headerImageView?.image = UIImage(named: "header_bg") 34 | headerImageView?.contentMode = UIViewContentMode.scaleAspectFill 35 | header.insertSubview(headerImageView, belowSubview: headerLabel) 36 | 37 | // Header - Blurred Image 38 | 39 | headerBlurImageView = UIImageView(frame: header.bounds) 40 | headerBlurImageView?.image = UIImage(named: "header_bg")?.blurredImage(withRadius: 10, iterations: 20, tintColor: UIColor.clear) 41 | headerBlurImageView?.contentMode = UIViewContentMode.scaleAspectFill 42 | headerBlurImageView?.alpha = 0.0 43 | header.insertSubview(headerBlurImageView, belowSubview: headerLabel) 44 | 45 | header.clipsToBounds = true 46 | } 47 | 48 | override func didReceiveMemoryWarning() { 49 | super.didReceiveMemoryWarning() 50 | } 51 | 52 | func scrollViewDidScroll(_ scrollView: UIScrollView) { 53 | 54 | let offset = scrollView.contentOffset.y 55 | var avatarTransform = CATransform3DIdentity 56 | var headerTransform = CATransform3DIdentity 57 | 58 | // PULL DOWN ----------------- 59 | 60 | if offset < 0 { 61 | 62 | let headerScaleFactor:CGFloat = -(offset) / header.bounds.height 63 | let headerSizevariation = ((header.bounds.height * (1.0 + headerScaleFactor)) - header.bounds.height)/2.0 64 | headerTransform = CATransform3DTranslate(headerTransform, 0, headerSizevariation, 0) 65 | headerTransform = CATransform3DScale(headerTransform, 1.0 + headerScaleFactor, 1.0 + headerScaleFactor, 0) 66 | 67 | header.layer.transform = headerTransform 68 | } 69 | 70 | // SCROLL UP/DOWN ------------ 71 | 72 | else { 73 | 74 | // Header ----------- 75 | 76 | headerTransform = CATransform3DTranslate(headerTransform, 0, max(-offset_HeaderStop, -offset), 0) 77 | 78 | // ------------ Label 79 | 80 | let labelTransform = CATransform3DMakeTranslation(0, max(-distance_W_LabelHeader, offset_B_LabelHeader - offset), 0) 81 | headerLabel.layer.transform = labelTransform 82 | 83 | // ------------ Blur 84 | 85 | headerBlurImageView?.alpha = min (1.0, (offset - offset_B_LabelHeader)/distance_W_LabelHeader) 86 | 87 | // Avatar ----------- 88 | 89 | let avatarScaleFactor = (min(offset_HeaderStop, offset)) / avatarImage.bounds.height / 1.4 // Slow down the animation 90 | let avatarSizeVariation = ((avatarImage.bounds.height * (1.0 + avatarScaleFactor)) - avatarImage.bounds.height) / 2.0 91 | avatarTransform = CATransform3DTranslate(avatarTransform, 0, avatarSizeVariation, 0) 92 | avatarTransform = CATransform3DScale(avatarTransform, 1.0 - avatarScaleFactor, 1.0 - avatarScaleFactor, 0) 93 | 94 | if offset <= offset_HeaderStop { 95 | 96 | if avatarImage.layer.zPosition < header.layer.zPosition{ 97 | header.layer.zPosition = 0 98 | } 99 | 100 | }else { 101 | if avatarImage.layer.zPosition >= header.layer.zPosition{ 102 | header.layer.zPosition = 2 103 | } 104 | } 105 | } 106 | 107 | // Apply Transformations 108 | 109 | header.layer.transform = headerTransform 110 | avatarImage.layer.transform = avatarTransform 111 | } 112 | 113 | @IBAction func shamelessActionThatBringsYouToMyTwitterProfile() { 114 | if UIApplication.shared.canOpenURL(URL(string:"twitter://user?screen_name=bitwaker")!){ 115 | UIApplication.shared.open(URL(string:"twitter://user?screen_name=bitwaker")!) 116 | }else{ 117 | UIApplication.shared.open(URL(string:"https://twitter.com/bitwaker")!) 118 | } 119 | } 120 | 121 | override var preferredStatusBarStyle: UIStatusBarStyle{ 122 | return UIStatusBarStyle.lightContent 123 | } 124 | } 125 | --------------------------------------------------------------------------------