├── .DS_Store ├── README.md ├── ScrollView-Header-stretching-Animations-Swift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── sanket.vaghela.xcuserdatad │ │ ├── IDEFindNavigatorScopes.plist │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── sanket.vaghela.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── ScrollView-Header-stretching-Animations-Swift ├── .DS_Store ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CompDetailsVC.swift ├── Custom-Class │ ├── ExpandableLabel.swift │ ├── UIButton-Custom.swift │ └── UIView-CornerRadius.swift ├── Image │ ├── btn_back.png │ ├── btn_join.png │ ├── icn_date.png │ ├── icn_details_top.png │ ├── icn_info.png │ ├── icn_no_1.png │ ├── icn_top_shadow.png │ └── image-placeholder.jpg ├── Info.plist └── ViewController.swift └── ScrollView-Header.gif /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ScrollView Stretch Header/ Parallax animation in Swift 5 3 | 4 | Example of how to create a UIScrollView HeaderView which sticks to the top and stretches when you pull down. 5 | 6 | ![](ScrollView-Header.gif) 7 | 8 | For understanding what I am doing here, you should be familiar with UIScrollView and its delegate methods. You also need to know about UIScrollView contentInset property. 9 | 10 | ## Getting Started 11 | 12 | Set the ScrollView contentInset property: 13 | ```swift 14 | @IBOutlet weak var scrView: UIScrollView! 15 | 16 | let scrollTopEdgeInsets:CGFloat = 200//scrollView Top insets size 17 | @IBOutlet weak var headerviewHeightConstraint: NSLayoutConstraint!//headerView height constraint 18 | 19 | scrView.delegate = self 20 | scrView.layer.cornerRadius = 25 21 | scrView.layer.masksToBounds = true 22 | scrView.contentInset = UIEdgeInsets(top: scrollTopEdgeInsets, left: 0, bottom: 0, right: 0) 23 | ``` 24 | And then tell the header view to react accordingly depending on the scrollviews content offset: 25 | 26 | ```swift 27 | func scrollViewDidScroll(_ scrollView: UIScrollView) 28 | { 29 | let minHeight:CGFloat = 50 30 | let maxHeight:CGFloat = 250+minHeight 31 | let yPos = scrView.contentOffset.y 32 | let newHeaderViewHeight = (maxHeight - yPos)-(maxHeight-minHeight) 33 | let tempNewHeaderViewHeight = (maxHeight - yPos)-(maxHeight-minHeight) 34 | let titleValue = newHeaderViewHeight-minHeight 35 | 36 | //set screen title alpha value 37 | if(titleValue > 0){ 38 | let finalValue = titleValue*100/scrollTopEdgeInsets 39 | let alphaValue = finalValue/100 40 | screenTitleLbl.alpha = 1-alphaValue 41 | } 42 | 43 | //manage header height constraint 44 | if (newHeaderViewHeight > maxHeight) { 45 | headerviewHeightConstraint.constant = (max(tempNewHeaderViewHeight, maxHeight)+(minHeight/2))+statusHeight 46 | } else { 47 | headerviewHeightConstraint.constant = (max(newHeaderViewHeight, minHeight)+(minHeight/2))+statusHeight 48 | } 49 | } 50 | ``` 51 | This is really simple. Check out the code for more. 52 | 53 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4402A59022FADECE00674BF2 /* icn_top_shadow.png in Resources */ = {isa = PBXBuildFile; fileRef = 4402A58422FADECE00674BF2 /* icn_top_shadow.png */; }; 11 | 4402A59122FADECE00674BF2 /* icn_info.png in Resources */ = {isa = PBXBuildFile; fileRef = 4402A58522FADECE00674BF2 /* icn_info.png */; }; 12 | 4402A59222FADECE00674BF2 /* btn_join.png in Resources */ = {isa = PBXBuildFile; fileRef = 4402A58622FADECE00674BF2 /* btn_join.png */; }; 13 | 4402A59322FADECE00674BF2 /* icn_date.png in Resources */ = {isa = PBXBuildFile; fileRef = 4402A58722FADECE00674BF2 /* icn_date.png */; }; 14 | 4402A59422FADECE00674BF2 /* icn_no_1.png in Resources */ = {isa = PBXBuildFile; fileRef = 4402A58822FADECE00674BF2 /* icn_no_1.png */; }; 15 | 4402A59522FADECE00674BF2 /* icn_details_top.png in Resources */ = {isa = PBXBuildFile; fileRef = 4402A58922FADECE00674BF2 /* icn_details_top.png */; }; 16 | 4402A59622FADECE00674BF2 /* btn_back.png in Resources */ = {isa = PBXBuildFile; fileRef = 4402A58A22FADECE00674BF2 /* btn_back.png */; }; 17 | 4402A59722FADECE00674BF2 /* image-placeholder.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4402A58B22FADECE00674BF2 /* image-placeholder.jpg */; }; 18 | 4402A59822FADECE00674BF2 /* ExpandableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4402A58D22FADECE00674BF2 /* ExpandableLabel.swift */; }; 19 | 4402A59922FADECE00674BF2 /* UIView-CornerRadius.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4402A58E22FADECE00674BF2 /* UIView-CornerRadius.swift */; }; 20 | 4402A59A22FADECE00674BF2 /* UIButton-Custom.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4402A58F22FADECE00674BF2 /* UIButton-Custom.swift */; }; 21 | 4402A59C22FADEE200674BF2 /* CompDetailsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4402A59B22FADEE200674BF2 /* CompDetailsVC.swift */; }; 22 | 4429A12622FADE660052A930 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4429A12522FADE660052A930 /* AppDelegate.swift */; }; 23 | 4429A12822FADE660052A930 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4429A12722FADE660052A930 /* ViewController.swift */; }; 24 | 4429A12B22FADE660052A930 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4429A12922FADE660052A930 /* Main.storyboard */; }; 25 | 4429A12D22FADE670052A930 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4429A12C22FADE670052A930 /* Assets.xcassets */; }; 26 | 4429A13022FADE670052A930 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4429A12E22FADE670052A930 /* LaunchScreen.storyboard */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 4402A58422FADECE00674BF2 /* icn_top_shadow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icn_top_shadow.png; sourceTree = ""; }; 31 | 4402A58522FADECE00674BF2 /* icn_info.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icn_info.png; sourceTree = ""; }; 32 | 4402A58622FADECE00674BF2 /* btn_join.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = btn_join.png; sourceTree = ""; }; 33 | 4402A58722FADECE00674BF2 /* icn_date.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icn_date.png; sourceTree = ""; }; 34 | 4402A58822FADECE00674BF2 /* icn_no_1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icn_no_1.png; sourceTree = ""; }; 35 | 4402A58922FADECE00674BF2 /* icn_details_top.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icn_details_top.png; sourceTree = ""; }; 36 | 4402A58A22FADECE00674BF2 /* btn_back.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = btn_back.png; sourceTree = ""; }; 37 | 4402A58B22FADECE00674BF2 /* image-placeholder.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = "image-placeholder.jpg"; sourceTree = ""; }; 38 | 4402A58D22FADECE00674BF2 /* ExpandableLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpandableLabel.swift; sourceTree = ""; }; 39 | 4402A58E22FADECE00674BF2 /* UIView-CornerRadius.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView-CornerRadius.swift"; sourceTree = ""; }; 40 | 4402A58F22FADECE00674BF2 /* UIButton-Custom.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIButton-Custom.swift"; sourceTree = ""; }; 41 | 4402A59B22FADEE200674BF2 /* CompDetailsVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompDetailsVC.swift; sourceTree = ""; }; 42 | 4429A12222FADE660052A930 /* ScrollView-Header-stretching-Animations-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ScrollView-Header-stretching-Animations-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 4429A12522FADE660052A930 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 44 | 4429A12722FADE660052A930 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 45 | 4429A12A22FADE660052A930 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 4429A12C22FADE670052A930 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 4429A12F22FADE670052A930 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 48 | 4429A13122FADE670052A930 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | /* End PBXFileReference section */ 50 | 51 | /* Begin PBXFrameworksBuildPhase section */ 52 | 4429A11F22FADE660052A930 /* Frameworks */ = { 53 | isa = PBXFrameworksBuildPhase; 54 | buildActionMask = 2147483647; 55 | files = ( 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXFrameworksBuildPhase section */ 60 | 61 | /* Begin PBXGroup section */ 62 | 4402A58322FADECE00674BF2 /* Image */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 4402A58422FADECE00674BF2 /* icn_top_shadow.png */, 66 | 4402A58522FADECE00674BF2 /* icn_info.png */, 67 | 4402A58622FADECE00674BF2 /* btn_join.png */, 68 | 4402A58722FADECE00674BF2 /* icn_date.png */, 69 | 4402A58822FADECE00674BF2 /* icn_no_1.png */, 70 | 4402A58922FADECE00674BF2 /* icn_details_top.png */, 71 | 4402A58A22FADECE00674BF2 /* btn_back.png */, 72 | 4402A58B22FADECE00674BF2 /* image-placeholder.jpg */, 73 | ); 74 | path = Image; 75 | sourceTree = ""; 76 | }; 77 | 4402A58C22FADECE00674BF2 /* Custom-Class */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 4402A58D22FADECE00674BF2 /* ExpandableLabel.swift */, 81 | 4402A58E22FADECE00674BF2 /* UIView-CornerRadius.swift */, 82 | 4402A58F22FADECE00674BF2 /* UIButton-Custom.swift */, 83 | ); 84 | path = "Custom-Class"; 85 | sourceTree = ""; 86 | }; 87 | 4429A11922FADE660052A930 = { 88 | isa = PBXGroup; 89 | children = ( 90 | 4429A12422FADE660052A930 /* ScrollView-Header-stretching-Animations-Swift */, 91 | 4429A12322FADE660052A930 /* Products */, 92 | ); 93 | sourceTree = ""; 94 | }; 95 | 4429A12322FADE660052A930 /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 4429A12222FADE660052A930 /* ScrollView-Header-stretching-Animations-Swift.app */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | 4429A12422FADE660052A930 /* ScrollView-Header-stretching-Animations-Swift */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 4429A12522FADE660052A930 /* AppDelegate.swift */, 107 | 4429A12722FADE660052A930 /* ViewController.swift */, 108 | 4402A59B22FADEE200674BF2 /* CompDetailsVC.swift */, 109 | 4429A12922FADE660052A930 /* Main.storyboard */, 110 | 4429A12C22FADE670052A930 /* Assets.xcassets */, 111 | 4429A12E22FADE670052A930 /* LaunchScreen.storyboard */, 112 | 4402A58C22FADECE00674BF2 /* Custom-Class */, 113 | 4402A58322FADECE00674BF2 /* Image */, 114 | 4429A13122FADE670052A930 /* Info.plist */, 115 | ); 116 | path = "ScrollView-Header-stretching-Animations-Swift"; 117 | sourceTree = ""; 118 | }; 119 | /* End PBXGroup section */ 120 | 121 | /* Begin PBXNativeTarget section */ 122 | 4429A12122FADE660052A930 /* ScrollView-Header-stretching-Animations-Swift */ = { 123 | isa = PBXNativeTarget; 124 | buildConfigurationList = 4429A13422FADE670052A930 /* Build configuration list for PBXNativeTarget "ScrollView-Header-stretching-Animations-Swift" */; 125 | buildPhases = ( 126 | 4429A11E22FADE660052A930 /* Sources */, 127 | 4429A11F22FADE660052A930 /* Frameworks */, 128 | 4429A12022FADE660052A930 /* Resources */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = "ScrollView-Header-stretching-Animations-Swift"; 135 | productName = "ScrollView-Header-stretching-Animations-Swift"; 136 | productReference = 4429A12222FADE660052A930 /* ScrollView-Header-stretching-Animations-Swift.app */; 137 | productType = "com.apple.product-type.application"; 138 | }; 139 | /* End PBXNativeTarget section */ 140 | 141 | /* Begin PBXProject section */ 142 | 4429A11A22FADE660052A930 /* Project object */ = { 143 | isa = PBXProject; 144 | attributes = { 145 | LastSwiftUpdateCheck = 1020; 146 | LastUpgradeCheck = 1020; 147 | ORGANIZATIONNAME = "Sanket Vaghela"; 148 | TargetAttributes = { 149 | 4429A12122FADE660052A930 = { 150 | CreatedOnToolsVersion = 10.2.1; 151 | }; 152 | }; 153 | }; 154 | buildConfigurationList = 4429A11D22FADE660052A930 /* Build configuration list for PBXProject "ScrollView-Header-stretching-Animations-Swift" */; 155 | compatibilityVersion = "Xcode 9.3"; 156 | developmentRegion = en; 157 | hasScannedForEncodings = 0; 158 | knownRegions = ( 159 | en, 160 | Base, 161 | ); 162 | mainGroup = 4429A11922FADE660052A930; 163 | productRefGroup = 4429A12322FADE660052A930 /* Products */; 164 | projectDirPath = ""; 165 | projectRoot = ""; 166 | targets = ( 167 | 4429A12122FADE660052A930 /* ScrollView-Header-stretching-Animations-Swift */, 168 | ); 169 | }; 170 | /* End PBXProject section */ 171 | 172 | /* Begin PBXResourcesBuildPhase section */ 173 | 4429A12022FADE660052A930 /* Resources */ = { 174 | isa = PBXResourcesBuildPhase; 175 | buildActionMask = 2147483647; 176 | files = ( 177 | 4402A59222FADECE00674BF2 /* btn_join.png in Resources */, 178 | 4429A13022FADE670052A930 /* LaunchScreen.storyboard in Resources */, 179 | 4402A59522FADECE00674BF2 /* icn_details_top.png in Resources */, 180 | 4429A12D22FADE670052A930 /* Assets.xcassets in Resources */, 181 | 4402A59022FADECE00674BF2 /* icn_top_shadow.png in Resources */, 182 | 4429A12B22FADE660052A930 /* Main.storyboard in Resources */, 183 | 4402A59422FADECE00674BF2 /* icn_no_1.png in Resources */, 184 | 4402A59322FADECE00674BF2 /* icn_date.png in Resources */, 185 | 4402A59722FADECE00674BF2 /* image-placeholder.jpg in Resources */, 186 | 4402A59122FADECE00674BF2 /* icn_info.png in Resources */, 187 | 4402A59622FADECE00674BF2 /* btn_back.png in Resources */, 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXResourcesBuildPhase section */ 192 | 193 | /* Begin PBXSourcesBuildPhase section */ 194 | 4429A11E22FADE660052A930 /* Sources */ = { 195 | isa = PBXSourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | 4402A59A22FADECE00674BF2 /* UIButton-Custom.swift in Sources */, 199 | 4429A12822FADE660052A930 /* ViewController.swift in Sources */, 200 | 4429A12622FADE660052A930 /* AppDelegate.swift in Sources */, 201 | 4402A59822FADECE00674BF2 /* ExpandableLabel.swift in Sources */, 202 | 4402A59C22FADEE200674BF2 /* CompDetailsVC.swift in Sources */, 203 | 4402A59922FADECE00674BF2 /* UIView-CornerRadius.swift in Sources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXSourcesBuildPhase section */ 208 | 209 | /* Begin PBXVariantGroup section */ 210 | 4429A12922FADE660052A930 /* Main.storyboard */ = { 211 | isa = PBXVariantGroup; 212 | children = ( 213 | 4429A12A22FADE660052A930 /* Base */, 214 | ); 215 | name = Main.storyboard; 216 | sourceTree = ""; 217 | }; 218 | 4429A12E22FADE670052A930 /* LaunchScreen.storyboard */ = { 219 | isa = PBXVariantGroup; 220 | children = ( 221 | 4429A12F22FADE670052A930 /* Base */, 222 | ); 223 | name = LaunchScreen.storyboard; 224 | sourceTree = ""; 225 | }; 226 | /* End PBXVariantGroup section */ 227 | 228 | /* Begin XCBuildConfiguration section */ 229 | 4429A13222FADE670052A930 /* Debug */ = { 230 | isa = XCBuildConfiguration; 231 | buildSettings = { 232 | ALWAYS_SEARCH_USER_PATHS = NO; 233 | CLANG_ANALYZER_NONNULL = YES; 234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 236 | CLANG_CXX_LIBRARY = "libc++"; 237 | CLANG_ENABLE_MODULES = YES; 238 | CLANG_ENABLE_OBJC_ARC = YES; 239 | CLANG_ENABLE_OBJC_WEAK = YES; 240 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 241 | CLANG_WARN_BOOL_CONVERSION = YES; 242 | CLANG_WARN_COMMA = YES; 243 | CLANG_WARN_CONSTANT_CONVERSION = YES; 244 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 245 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 246 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 247 | CLANG_WARN_EMPTY_BODY = YES; 248 | CLANG_WARN_ENUM_CONVERSION = YES; 249 | CLANG_WARN_INFINITE_RECURSION = YES; 250 | CLANG_WARN_INT_CONVERSION = YES; 251 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 252 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 253 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 255 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 256 | CLANG_WARN_STRICT_PROTOTYPES = YES; 257 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 258 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 259 | CLANG_WARN_UNREACHABLE_CODE = YES; 260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 261 | CODE_SIGN_IDENTITY = "iPhone Developer"; 262 | COPY_PHASE_STRIP = NO; 263 | DEBUG_INFORMATION_FORMAT = dwarf; 264 | ENABLE_STRICT_OBJC_MSGSEND = YES; 265 | ENABLE_TESTABILITY = YES; 266 | GCC_C_LANGUAGE_STANDARD = gnu11; 267 | GCC_DYNAMIC_NO_PIC = NO; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_OPTIMIZATION_LEVEL = 0; 270 | GCC_PREPROCESSOR_DEFINITIONS = ( 271 | "DEBUG=1", 272 | "$(inherited)", 273 | ); 274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 276 | GCC_WARN_UNDECLARED_SELECTOR = YES; 277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 278 | GCC_WARN_UNUSED_FUNCTION = YES; 279 | GCC_WARN_UNUSED_VARIABLE = YES; 280 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 281 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 282 | MTL_FAST_MATH = YES; 283 | ONLY_ACTIVE_ARCH = YES; 284 | SDKROOT = iphoneos; 285 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 286 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 287 | }; 288 | name = Debug; 289 | }; 290 | 4429A13322FADE670052A930 /* Release */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ALWAYS_SEARCH_USER_PATHS = NO; 294 | CLANG_ANALYZER_NONNULL = YES; 295 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 297 | CLANG_CXX_LIBRARY = "libc++"; 298 | CLANG_ENABLE_MODULES = YES; 299 | CLANG_ENABLE_OBJC_ARC = YES; 300 | CLANG_ENABLE_OBJC_WEAK = YES; 301 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 302 | CLANG_WARN_BOOL_CONVERSION = YES; 303 | CLANG_WARN_COMMA = YES; 304 | CLANG_WARN_CONSTANT_CONVERSION = YES; 305 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 306 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 307 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 308 | CLANG_WARN_EMPTY_BODY = YES; 309 | CLANG_WARN_ENUM_CONVERSION = YES; 310 | CLANG_WARN_INFINITE_RECURSION = YES; 311 | CLANG_WARN_INT_CONVERSION = YES; 312 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 313 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 314 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 315 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 316 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 317 | CLANG_WARN_STRICT_PROTOTYPES = YES; 318 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 319 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 320 | CLANG_WARN_UNREACHABLE_CODE = YES; 321 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 322 | CODE_SIGN_IDENTITY = "iPhone Developer"; 323 | COPY_PHASE_STRIP = NO; 324 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 325 | ENABLE_NS_ASSERTIONS = NO; 326 | ENABLE_STRICT_OBJC_MSGSEND = YES; 327 | GCC_C_LANGUAGE_STANDARD = gnu11; 328 | GCC_NO_COMMON_BLOCKS = YES; 329 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 330 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 331 | GCC_WARN_UNDECLARED_SELECTOR = YES; 332 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 333 | GCC_WARN_UNUSED_FUNCTION = YES; 334 | GCC_WARN_UNUSED_VARIABLE = YES; 335 | IPHONEOS_DEPLOYMENT_TARGET = 12.2; 336 | MTL_ENABLE_DEBUG_INFO = NO; 337 | MTL_FAST_MATH = YES; 338 | SDKROOT = iphoneos; 339 | SWIFT_COMPILATION_MODE = wholemodule; 340 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 341 | VALIDATE_PRODUCT = YES; 342 | }; 343 | name = Release; 344 | }; 345 | 4429A13522FADE670052A930 /* Debug */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 349 | CODE_SIGN_STYLE = Automatic; 350 | DEVELOPMENT_TEAM = 37QRR9N3P9; 351 | INFOPLIST_FILE = "ScrollView-Header-stretching-Animations-Swift/Info.plist"; 352 | LD_RUNPATH_SEARCH_PATHS = ( 353 | "$(inherited)", 354 | "@executable_path/Frameworks", 355 | ); 356 | PRODUCT_BUNDLE_IDENTIFIER = "artoon.ScrollView-Header-stretching-Animations-Swift"; 357 | PRODUCT_NAME = "$(TARGET_NAME)"; 358 | SWIFT_VERSION = 5.0; 359 | TARGETED_DEVICE_FAMILY = "1,2"; 360 | }; 361 | name = Debug; 362 | }; 363 | 4429A13622FADE670052A930 /* Release */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 367 | CODE_SIGN_STYLE = Automatic; 368 | DEVELOPMENT_TEAM = 37QRR9N3P9; 369 | INFOPLIST_FILE = "ScrollView-Header-stretching-Animations-Swift/Info.plist"; 370 | LD_RUNPATH_SEARCH_PATHS = ( 371 | "$(inherited)", 372 | "@executable_path/Frameworks", 373 | ); 374 | PRODUCT_BUNDLE_IDENTIFIER = "artoon.ScrollView-Header-stretching-Animations-Swift"; 375 | PRODUCT_NAME = "$(TARGET_NAME)"; 376 | SWIFT_VERSION = 5.0; 377 | TARGETED_DEVICE_FAMILY = "1,2"; 378 | }; 379 | name = Release; 380 | }; 381 | /* End XCBuildConfiguration section */ 382 | 383 | /* Begin XCConfigurationList section */ 384 | 4429A11D22FADE660052A930 /* Build configuration list for PBXProject "ScrollView-Header-stretching-Animations-Swift" */ = { 385 | isa = XCConfigurationList; 386 | buildConfigurations = ( 387 | 4429A13222FADE670052A930 /* Debug */, 388 | 4429A13322FADE670052A930 /* Release */, 389 | ); 390 | defaultConfigurationIsVisible = 0; 391 | defaultConfigurationName = Release; 392 | }; 393 | 4429A13422FADE670052A930 /* Build configuration list for PBXNativeTarget "ScrollView-Header-stretching-Animations-Swift" */ = { 394 | isa = XCConfigurationList; 395 | buildConfigurations = ( 396 | 4429A13522FADE670052A930 /* Debug */, 397 | 4429A13622FADE670052A930 /* Release */, 398 | ); 399 | defaultConfigurationIsVisible = 0; 400 | defaultConfigurationName = Release; 401 | }; 402 | /* End XCConfigurationList section */ 403 | }; 404 | rootObject = 4429A11A22FADE660052A930 /* Project object */; 405 | } 406 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift.xcodeproj/project.xcworkspace/xcuserdata/sanket.vaghela.xcuserdatad/IDEFindNavigatorScopes.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift.xcodeproj/project.xcworkspace/xcuserdata/sanket.vaghela.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift.xcodeproj/project.xcworkspace/xcuserdata/sanket.vaghela.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift.xcodeproj/xcuserdata/sanket.vaghela.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ScrollView-Header-stretching-Animations-Swift.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift/.DS_Store -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ScrollView-Header-stretching-Animations-Swift 4 | // 5 | // Created by Artoon Solutions Private Limited on 07/08/19. 6 | // Copyright © 2019 Artoon Solutions Private Limited. 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: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/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 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Bahnschrift 15 | Bahnschrift_Bold 16 | Bahnschrift_SemiBold 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 76 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 109 | 117 | 118 | 119 | 120 | 126 | 132 | 138 | 144 | 150 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 198 | 207 | 208 | 209 | 210 | 216 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 255 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 340 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 386 | 392 | 393 | 394 | 395 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 421 | 422 | 423 | 424 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 448 | 449 | 450 | 451 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 475 | 476 | 477 | 478 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 502 | 503 | 504 | 505 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 678 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/CompDetailsVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CompDetailsVC.swift 3 | // Sportily 4 | // 5 | // Created by Artoon Solutions Private Limited on 25/07/19. 6 | // Copyright © 2019 Artoon Solutions Private Limited. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class CompDetailsVC: UIViewController,UIScrollViewDelegate,UICollectionViewDataSource,UICollectionViewDelegate,UITableViewDelegate,UITableViewDataSource { 12 | 13 | @IBOutlet weak var headerView: UIView! 14 | @IBOutlet weak var scrView: UIScrollView! 15 | @IBOutlet weak var roundCollectionView: UICollectionView! 16 | @IBOutlet weak var leaderboardTblView: UITableView! 17 | @IBOutlet weak var innerView: UIView! 18 | @IBOutlet weak var screenTitleLbl: UILabel! 19 | @IBOutlet weak var detailsLbl: ExpandableLabel! 20 | 21 | @IBOutlet weak var scrollViewTopConstraint: NSLayoutConstraint! 22 | @IBOutlet weak var tblHeightConstraint: NSLayoutConstraint! 23 | @IBOutlet weak var headerviewHeightConstraint: NSLayoutConstraint! 24 | @IBOutlet weak var titleTopConstraint: NSLayoutConstraint! 25 | 26 | var statusHeight = UIApplication.shared.statusBarFrame.size.height//Statusbar height 27 | let scrollTopEdgeInsets:CGFloat = 200//scrollView Top insets size 28 | 29 | override func viewDidLoad() { 30 | super.viewDidLoad() 31 | scrView.delegate = self 32 | scrView.layer.cornerRadius = 25 33 | scrView.layer.masksToBounds = true 34 | scrView.contentInset = UIEdgeInsets(top: scrollTopEdgeInsets, left: 0, bottom: 0, right: 0) 35 | 36 | roundCollectionView.delegate = self 37 | roundCollectionView.dataSource = self 38 | 39 | leaderboardTblView.delegate = self 40 | leaderboardTblView.dataSource = self 41 | 42 | //Set Expandable Lable 43 | let numberofline = 2 44 | detailsLbl.shouldCollapse = true 45 | detailsLbl.setLessLinkWith(lessLink: "View Less", attributes: [.foregroundColor:UIColor(red: 66/255, green: 67/255, blue: 106/255, alpha: 1)], position: nil) 46 | detailsLbl.textReplacementType = ExpandableLabel.TextReplacementType.character 47 | detailsLbl.numberOfLines = UIScreen.main.bounds.height >= 768 ? numberofline * 2 : numberofline 48 | detailsLbl.collapsed = true 49 | detailsLbl.text = "The 2019 AFL season starts on Thursday 21st of March with the tradition richmond versus carlton blockbuster. The season will run from Round 1 starting this week through 23 weeks of exciting football concluding on Saturday" 50 | 51 | //set headerview height 52 | headerviewHeightConstraint.constant = headerviewHeightConstraint.constant + statusHeight + 44 53 | } 54 | 55 | override func viewDidAppear(_ animated: Bool) { 56 | tblHeightConstraint.constant = self.leaderboardTblView.contentSize.height 57 | } 58 | 59 | func scrollViewDidScroll(_ scrollView: UIScrollView) 60 | { 61 | let minHeight:CGFloat = 50 62 | let maxHeight:CGFloat = 250+minHeight 63 | let yPos = scrView.contentOffset.y 64 | let newHeaderViewHeight = (maxHeight - yPos)-(maxHeight-minHeight) 65 | let tempNewHeaderViewHeight = (maxHeight - yPos)-(maxHeight-minHeight) 66 | let titleValue = newHeaderViewHeight-minHeight 67 | 68 | //set screen title alpha value 69 | if(titleValue > 0){ 70 | let finalValue = titleValue*100/scrollTopEdgeInsets 71 | let alphaValue = finalValue/100 72 | screenTitleLbl.alpha = 1-alphaValue 73 | } 74 | 75 | //manage header height constraint 76 | if (newHeaderViewHeight > maxHeight) { 77 | headerviewHeightConstraint.constant = (max(tempNewHeaderViewHeight, maxHeight)+(minHeight/2))+statusHeight 78 | } else { 79 | headerviewHeightConstraint.constant = (max(newHeaderViewHeight, minHeight)+(minHeight/2))+statusHeight 80 | } 81 | } 82 | 83 | 84 | // MARK: - CollectionView Data Set 85 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 86 | return 10 87 | } 88 | 89 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 90 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "RoundCollectionCell", for: indexPath) as! RoundCollectionCell 91 | return cell 92 | } 93 | 94 | // MARK: - TableView Data Set 95 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 96 | return 3 97 | } 98 | 99 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 100 | let cell = tableView.dequeueReusableCell(withIdentifier: "LeaderboardTblCell", for: indexPath) as! LeaderboardTblCell 101 | return cell 102 | } 103 | 104 | // MARK: - ExpandableLabel 105 | func willExpandLabel(_ label: ExpandableLabel) { 106 | 107 | } 108 | 109 | func didExpandLabel(_ label: ExpandableLabel) { 110 | 111 | } 112 | 113 | func willCollapseLabel(_ label: ExpandableLabel) { 114 | 115 | } 116 | 117 | func didCollapseLabel(_ label: ExpandableLabel) { 118 | 119 | } 120 | func shouldCollapseLabel(_ label: ExpandableLabel) -> Bool { 121 | return true 122 | } 123 | 124 | override var prefersStatusBarHidden: Bool { 125 | return true 126 | } 127 | 128 | } 129 | 130 | class RoundCollectionCell:UICollectionViewCell 131 | { 132 | 133 | } 134 | 135 | class LeaderboardTblCell: UITableViewCell { 136 | @IBOutlet weak var userImage: UIImageView! 137 | 138 | override func awakeFromNib() { 139 | super.awakeFromNib() 140 | userImage.layer.cornerRadius = 5 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Custom-Class/ExpandableLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExpandableLabel.swift 3 | // 4 | // Copyright (c) 2015 apploft. GmbH 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | typealias LineIndexTuple = (line: CTLine, index: Int) 25 | 26 | import UIKit 27 | 28 | /** 29 | * The delegate of ExpandableLabel. 30 | */ 31 | @objc public protocol ExpandableLabelDelegate: NSObjectProtocol { 32 | @objc func willExpandLabel(_ label: ExpandableLabel) 33 | @objc func didExpandLabel(_ label: ExpandableLabel) 34 | @objc func willCollapseLabel(_ label: ExpandableLabel) 35 | @objc func didCollapseLabel(_ label: ExpandableLabel) 36 | } 37 | 38 | /** 39 | * ExpandableLabel 40 | */ 41 | @objc open class ExpandableLabel: UILabel { 42 | public enum TextReplacementType { 43 | case character 44 | case word 45 | } 46 | 47 | /// The delegate of ExpandableLabel 48 | @objc weak open var delegate: ExpandableLabelDelegate? 49 | 50 | /// Set 'true' if the label should be collapsed or 'false' for expanded. 51 | @IBInspectable open var collapsed: Bool = true { 52 | didSet { 53 | super.attributedText = (collapsed) ? self.collapsedText : self.expandedText 54 | super.numberOfLines = (collapsed) ? self.collapsedNumberOfLines : 0 55 | if let animationView = animationView { 56 | UIView.animate(withDuration: 0.5) { 57 | animationView.layoutIfNeeded() 58 | } 59 | } 60 | } 61 | } 62 | 63 | /// Set 'true' if the label can be expanded or 'false' if not. 64 | /// The default value is 'true'. 65 | @IBInspectable open var shouldExpand: Bool = true 66 | 67 | /// Set 'true' if the label can be collapsed or 'false' if not. 68 | /// The default value is 'false'. 69 | @IBInspectable open var shouldCollapse: Bool = false 70 | 71 | /// Set the link name (and attributes) that is shown when collapsed. 72 | /// The default value is "More". Cannot be nil. 73 | @objc open var collapsedAttributedLink: NSAttributedString! { 74 | didSet { 75 | self.collapsedAttributedLink = collapsedAttributedLink.copyWithAddedFontAttribute(font) 76 | } 77 | } 78 | 79 | /// Set the link name (and attributes) that is shown when expanded. 80 | /// The default value is "Less". Can be nil. 81 | @objc open var expandedAttributedLink: NSAttributedString? 82 | 83 | /// Set the ellipsis that appears just after the text and before the link. 84 | /// The default value is "...". Can be nil. 85 | @objc open var ellipsis: NSAttributedString? { 86 | didSet { 87 | self.ellipsis = ellipsis?.copyWithAddedFontAttribute(font) 88 | } 89 | } 90 | 91 | /// Set a view to animate changes of the label collapsed state with. If this value is nil, no animation occurs. 92 | /// Usually you assign the superview of this label or a UIScrollView in which this label sits. 93 | /// Also don't forget to set the contentMode of this label to top to smoothly reveal the hidden lines. 94 | /// The default value is 'nil'. 95 | @objc open var animationView: UIView? 96 | 97 | open var textReplacementType: TextReplacementType = .word 98 | 99 | private var collapsedText: NSAttributedString? 100 | private var linkHighlighted: Bool = false 101 | private let touchSize = CGSize(width: 44, height: 44) 102 | private var linkRect: CGRect? 103 | private var collapsedNumberOfLines: NSInteger = 0 104 | private var expandedLinkPosition: NSTextAlignment? 105 | private var collapsedLinkTextRange: NSRange? 106 | private var expandedLinkTextRange: NSRange? 107 | 108 | open override var numberOfLines: NSInteger { 109 | didSet { 110 | collapsedNumberOfLines = numberOfLines 111 | } 112 | } 113 | 114 | @objc public required init?(coder aDecoder: NSCoder) { 115 | super.init(coder: aDecoder) 116 | commonInit() 117 | } 118 | 119 | @objc public override init(frame: CGRect) { 120 | super.init(frame: frame) 121 | self.commonInit() 122 | } 123 | 124 | @objc public init() { 125 | super.init(frame: .zero) 126 | } 127 | 128 | open override var text: String? { 129 | set(text) { 130 | if let text = text { 131 | self.attributedText = NSAttributedString(string: text) 132 | } else { 133 | self.attributedText = nil 134 | } 135 | } 136 | get { 137 | return self.attributedText?.string 138 | } 139 | } 140 | 141 | open private(set) var expandedText: NSAttributedString? 142 | open override var attributedText: NSAttributedString? { 143 | set(attributedText) { 144 | if let attributedText = attributedText?.copyWithAddedFontAttribute(font).copyWithParagraphAttribute(font), 145 | attributedText.length > 0 { 146 | self.collapsedText = getCollapsedText(for: attributedText, link: (linkHighlighted) ? collapsedAttributedLink.copyWithHighlightedColor() : self.collapsedAttributedLink) 147 | self.expandedText = getExpandedText(for: attributedText, link: (linkHighlighted) ? expandedAttributedLink?.copyWithHighlightedColor() : self.expandedAttributedLink) 148 | super.attributedText = (self.collapsed) ? self.collapsedText : self.expandedText 149 | } else { 150 | self.expandedText = nil 151 | self.collapsedText = nil 152 | super.attributedText = nil 153 | } 154 | } 155 | get { 156 | return super.attributedText 157 | } 158 | } 159 | 160 | open func setLessLinkWith(lessLink: String, attributes: [NSAttributedString.Key: AnyObject], position: NSTextAlignment?) { 161 | var alignedattributes = attributes 162 | if let pos = position { 163 | expandedLinkPosition = pos 164 | let titleParagraphStyle = NSMutableParagraphStyle() 165 | titleParagraphStyle.alignment = pos 166 | alignedattributes[.paragraphStyle] = titleParagraphStyle 167 | } 168 | expandedAttributedLink = NSMutableAttributedString(string: lessLink, 169 | attributes: alignedattributes) 170 | } 171 | } 172 | 173 | // MARK: - Touch Handling 174 | 175 | extension ExpandableLabel { 176 | 177 | open override func touchesBegan(_ touches: Set, with event: UIEvent?) { 178 | setLinkHighlighted(touches, event: event, highlighted: true) 179 | } 180 | 181 | open override func touchesCancelled(_ touches: Set, with event: UIEvent?) { 182 | setLinkHighlighted(touches, event: event, highlighted: false) 183 | } 184 | 185 | open override func touchesEnded(_ touches: Set, with event: UIEvent?) { 186 | guard let touch = touches.first else { 187 | return 188 | } 189 | 190 | if !collapsed { 191 | guard let range = self.expandedLinkTextRange else { 192 | return 193 | } 194 | 195 | if shouldCollapse && check(touch: touch, isInRange: range) { 196 | delegate?.willCollapseLabel(self) 197 | collapsed = true 198 | delegate?.didCollapseLabel(self) 199 | linkHighlighted = isHighlighted 200 | setNeedsDisplay() 201 | } 202 | } else { 203 | if shouldExpand && setLinkHighlighted(touches, event: event, highlighted: false) { 204 | delegate?.willExpandLabel(self) 205 | collapsed = false 206 | delegate?.didExpandLabel(self) 207 | } 208 | } 209 | } 210 | 211 | open override func touchesMoved(_ touches: Set, with event: UIEvent?) { 212 | setLinkHighlighted(touches, event: event, highlighted: false) 213 | } 214 | } 215 | 216 | // MARK: Privates 217 | 218 | extension ExpandableLabel { 219 | private func commonInit() { 220 | isUserInteractionEnabled = true 221 | lineBreakMode = .byClipping 222 | collapsedNumberOfLines = numberOfLines 223 | expandedAttributedLink = nil 224 | collapsedAttributedLink = NSAttributedString(string: "View More", attributes: [.foregroundColor:UIColor(red: 66/255, green: 67/255, blue: 106/255, alpha: 1)]) 225 | ellipsis = NSAttributedString(string: "...") 226 | //.font: UIFont.boldSystemFont(ofSize: font.pointSize), 227 | } 228 | 229 | private func textReplaceWordWithLink(_ lineIndex: LineIndexTuple, text: NSAttributedString, linkName: NSAttributedString) -> NSAttributedString { 230 | let lineText = text.text(for: lineIndex.line) 231 | var lineTextWithLink = lineText 232 | (lineText.string as NSString).enumerateSubstrings(in: NSRange(location: 0, length: lineText.length), options: [.byWords, .reverse]) { (word, subRange, enclosingRange, stop) -> Void in 233 | let lineTextWithLastWordRemoved = lineText.attributedSubstring(from: NSRange(location: 0, length: subRange.location)) 234 | let lineTextWithAddedLink = NSMutableAttributedString(attributedString: lineTextWithLastWordRemoved) 235 | if let ellipsis = self.ellipsis { 236 | lineTextWithAddedLink.append(ellipsis) 237 | lineTextWithAddedLink.append(NSAttributedString(string: " ", attributes: [.font: self.font as Any])) 238 | } 239 | lineTextWithAddedLink.append(linkName) 240 | let fits = self.textFitsWidth(lineTextWithAddedLink) 241 | if fits { 242 | lineTextWithLink = lineTextWithAddedLink 243 | let lineTextWithLastWordRemovedRect = lineTextWithLastWordRemoved.boundingRect(for: self.frame.size.width) 244 | let wordRect = linkName.boundingRect(for: self.frame.size.width) 245 | let width = lineTextWithLastWordRemoved.string == "" ? self.frame.width : wordRect.size.width 246 | self.linkRect = CGRect(x: lineTextWithLastWordRemovedRect.size.width, y: self.font.lineHeight * CGFloat(lineIndex.index), width: width, height: wordRect.size.height) 247 | stop.pointee = true 248 | } 249 | } 250 | return lineTextWithLink 251 | } 252 | 253 | private func textReplaceWithLink(_ lineIndex: LineIndexTuple, text: NSAttributedString, linkName: NSAttributedString) -> NSAttributedString { 254 | let lineText = text.text(for: lineIndex.line) 255 | let lineTextTrimmedNewLines = NSMutableAttributedString() 256 | lineTextTrimmedNewLines.append(lineText) 257 | let nsString = lineTextTrimmedNewLines.string as NSString 258 | let range = nsString.rangeOfCharacter(from: CharacterSet.newlines) 259 | if range.length > 0 { 260 | lineTextTrimmedNewLines.replaceCharacters(in: range, with: "") 261 | } 262 | let linkText = NSMutableAttributedString() 263 | if let ellipsis = self.ellipsis { 264 | linkText.append(ellipsis) 265 | linkText.append(NSAttributedString(string: " ", attributes: [.font: self.font as Any])) 266 | } 267 | linkText.append(linkName) 268 | 269 | let lengthDifference = lineTextTrimmedNewLines.string.composedCount - linkText.string.composedCount 270 | let truncatedString = lineTextTrimmedNewLines.attributedSubstring( 271 | from: NSMakeRange(0, lengthDifference >= 0 ? lengthDifference : lineTextTrimmedNewLines.string.composedCount)) 272 | let lineTextWithLink = NSMutableAttributedString(attributedString: truncatedString) 273 | lineTextWithLink.append(linkText) 274 | return lineTextWithLink 275 | } 276 | 277 | private func getExpandedText(for text: NSAttributedString?, link: NSAttributedString?) -> NSAttributedString? { 278 | guard let text = text else { return nil } 279 | let expandedText = NSMutableAttributedString() 280 | expandedText.append(text) 281 | if let link = link, textWillBeTruncated(expandedText) { 282 | let spaceOrNewLine = expandedLinkPosition == nil ? " " : "\n" 283 | expandedText.append(NSAttributedString(string: "\(spaceOrNewLine)")) 284 | expandedText.append(NSMutableAttributedString(string: "\(link.string)", attributes: link.attributes(at: 0, effectiveRange: nil)).copyWithAddedFontAttribute(font)) 285 | expandedLinkTextRange = NSMakeRange(expandedText.length - link.length, link.length) 286 | } 287 | 288 | return expandedText 289 | } 290 | 291 | private func getCollapsedText(for text: NSAttributedString?, link: NSAttributedString) -> NSAttributedString? { 292 | guard let text = text else { return nil } 293 | let lines = text.lines(for: frame.size.width) 294 | if collapsedNumberOfLines > 0 && collapsedNumberOfLines < lines.count { 295 | let lastLineRef = lines[collapsedNumberOfLines-1] as CTLine 296 | var lineIndex: LineIndexTuple? 297 | var modifiedLastLineText: NSAttributedString? 298 | 299 | if self.textReplacementType == .word { 300 | lineIndex = findLineWithWords(lastLine: lastLineRef, text: text, lines: lines) 301 | if let lineIndex = lineIndex { 302 | modifiedLastLineText = textReplaceWordWithLink(lineIndex, text: text, linkName: link) 303 | } 304 | } else { 305 | lineIndex = (lastLineRef, collapsedNumberOfLines - 1) 306 | if let lineIndex = lineIndex { 307 | modifiedLastLineText = textReplaceWithLink(lineIndex, text: text, linkName: link) 308 | } 309 | } 310 | 311 | if let lineIndex = lineIndex, let modifiedLastLineText = modifiedLastLineText { 312 | let collapsedLines = NSMutableAttributedString() 313 | for index in 0.. LineIndexTuple { 328 | var lastLineRef = lastLine 329 | var lastLineIndex = collapsedNumberOfLines - 1 330 | var lineWords = spiltIntoWords(str: text.text(for: lastLineRef).string as NSString) 331 | while lineWords.count < 2 && lastLineIndex > 0 { 332 | lastLineIndex -= 1 333 | lastLineRef = lines[lastLineIndex] as CTLine 334 | lineWords = spiltIntoWords(str: text.text(for: lastLineRef).string as NSString) 335 | } 336 | return (lastLineRef, lastLineIndex) 337 | } 338 | 339 | private func spiltIntoWords(str: NSString) -> [String] { 340 | var strings: [String] = [] 341 | str.enumerateSubstrings(in: NSRange(location: 0, length: str.length), options: [.byWords, .reverse]) { (word, subRange, enclosingRange, stop) -> Void in 342 | if let unwrappedWord = word { 343 | strings.append(unwrappedWord) 344 | } 345 | if strings.count > 1 { stop.pointee = true } 346 | } 347 | return strings 348 | } 349 | 350 | private func textFitsWidth(_ text: NSAttributedString) -> Bool { 351 | return (text.boundingRect(for: frame.size.width).size.height <= font.lineHeight) as Bool 352 | } 353 | 354 | private func textWillBeTruncated(_ text: NSAttributedString) -> Bool { 355 | let lines = text.lines(for: frame.size.width) 356 | return collapsedNumberOfLines > 0 && collapsedNumberOfLines < lines.count 357 | } 358 | 359 | private func textClicked(touches: Set?, event: UIEvent?) -> Bool { 360 | let touch = event?.allTouches?.first 361 | let location = touch?.location(in: self) 362 | let textRect = self.attributedText?.boundingRect(for: self.frame.width) 363 | if let location = location, let textRect = textRect { 364 | let finger = CGRect(x: location.x-touchSize.width/2, y: location.y-touchSize.height/2, width: touchSize.width, height: touchSize.height) 365 | if finger.intersects(textRect) { 366 | return true 367 | } 368 | } 369 | return false 370 | } 371 | 372 | @discardableResult private func setLinkHighlighted(_ touches: Set?, event: UIEvent?, highlighted: Bool) -> Bool { 373 | guard let touch = touches?.first else { 374 | return false 375 | } 376 | 377 | guard let range = self.collapsedLinkTextRange else { 378 | return false 379 | } 380 | 381 | if collapsed && check(touch: touch, isInRange: range) { 382 | linkHighlighted = highlighted 383 | setNeedsDisplay() 384 | return true 385 | } 386 | return false 387 | } 388 | } 389 | 390 | // MARK: Convenience Methods 391 | 392 | private extension NSAttributedString { 393 | func hasFontAttribute() -> Bool { 394 | guard !self.string.isEmpty else { return false } 395 | let font = self.attribute(.font, at: 0, effectiveRange: nil) as? UIFont 396 | return font != nil 397 | } 398 | 399 | func copyWithParagraphAttribute(_ font: UIFont) -> NSAttributedString { 400 | let paragraphStyle = NSMutableParagraphStyle() 401 | paragraphStyle.lineHeightMultiple = 1.05 402 | paragraphStyle.alignment = .left 403 | paragraphStyle.lineSpacing = 0.0 404 | paragraphStyle.minimumLineHeight = font.lineHeight 405 | paragraphStyle.maximumLineHeight = font.lineHeight 406 | 407 | let copy = NSMutableAttributedString(attributedString: self) 408 | let range = NSRange(location: 0, length: copy.length) 409 | copy.addAttribute(.paragraphStyle, value: paragraphStyle, range: range) 410 | copy.addAttribute(.baselineOffset, value: font.pointSize * 0.08, range: range) 411 | return copy 412 | } 413 | 414 | func copyWithAddedFontAttribute(_ font: UIFont) -> NSAttributedString { 415 | if !hasFontAttribute() { 416 | let copy = NSMutableAttributedString(attributedString: self) 417 | copy.addAttribute(.font, value: font, range: NSRange(location: 0, length: copy.length)) 418 | return copy 419 | } 420 | return self.copy() as! NSAttributedString 421 | } 422 | 423 | func copyWithHighlightedColor() -> NSAttributedString { 424 | let alphaComponent = CGFloat(0.5) 425 | let baseColor: UIColor = (self.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor)?.withAlphaComponent(alphaComponent) ?? 426 | UIColor.black.withAlphaComponent(alphaComponent) 427 | let highlightedCopy = NSMutableAttributedString(attributedString: self) 428 | let range = NSRange(location: 0, length: highlightedCopy.length) 429 | highlightedCopy.removeAttribute(.foregroundColor, range: range) 430 | highlightedCopy.addAttribute(.foregroundColor, value: baseColor, range: range) 431 | return highlightedCopy 432 | } 433 | 434 | func lines(for width: CGFloat) -> [CTLine] { 435 | let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: width, height: .greatestFiniteMagnitude)) 436 | let frameSetterRef: CTFramesetter = CTFramesetterCreateWithAttributedString(self as CFAttributedString) 437 | let frameRef: CTFrame = CTFramesetterCreateFrame(frameSetterRef, CFRange(location: 0, length: 0), path.cgPath, nil) 438 | 439 | let linesNS: NSArray = CTFrameGetLines(frameRef) 440 | let linesAO: [AnyObject] = linesNS as [AnyObject] 441 | let lines: [CTLine] = linesAO as! [CTLine] 442 | 443 | return lines 444 | } 445 | 446 | func text(for lineRef: CTLine) -> NSAttributedString { 447 | let lineRangeRef: CFRange = CTLineGetStringRange(lineRef) 448 | let range: NSRange = NSRange(location: lineRangeRef.location, length: lineRangeRef.length) 449 | return self.attributedSubstring(from: range) 450 | } 451 | 452 | func boundingRect(for width: CGFloat) -> CGRect { 453 | return self.boundingRect(with: CGSize(width: width, height: .greatestFiniteMagnitude), 454 | options: .usesLineFragmentOrigin, context: nil) 455 | } 456 | } 457 | 458 | extension String { 459 | var composedCount : Int { 460 | var count = 0 461 | enumerateSubstrings(in: startIndex.. Bool { 468 | let touchPoint = touch.location(in: self) 469 | let index = characterIndex(at: touchPoint) 470 | return NSLocationInRange(index, targetRange) 471 | } 472 | 473 | private func characterIndex(at touchPoint: CGPoint) -> Int { 474 | guard let attributedString = attributedText else { return NSNotFound } 475 | if !bounds.contains(touchPoint) { 476 | return NSNotFound 477 | } 478 | 479 | let textRect = self.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines) 480 | if !textRect.contains(touchPoint) { 481 | return NSNotFound 482 | } 483 | 484 | var point = touchPoint 485 | // Offset tap coordinates by textRect origin to make them relative to the origin of frame 486 | point = CGPoint(x: point.x - textRect.origin.x, y: point.y - textRect.origin.y) 487 | // Convert tap coordinates (start at top left) to CT coordinates (start at bottom left) 488 | point = CGPoint(x: point.x, y: textRect.size.height - point.y) 489 | 490 | let framesetter = CTFramesetterCreateWithAttributedString(attributedString) 491 | let suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, attributedString.length), nil, CGSize(width: textRect.width, height: CGFloat.greatestFiniteMagnitude), nil) 492 | 493 | let path = CGMutablePath() 494 | path.addRect(CGRect(x: 0, y: 0, width: suggestedSize.width, height: CGFloat(ceilf(Float(suggestedSize.height))))) 495 | 496 | let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedString.length), path, nil) 497 | let lines = CTFrameGetLines(frame) 498 | let linesCount = numberOfLines > 0 ? min(numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines) 499 | if linesCount == 0 { 500 | return NSNotFound 501 | } 502 | 503 | var lineOrigins = [CGPoint](repeating: .zero, count: linesCount) 504 | CTFrameGetLineOrigins(frame, CFRangeMake(0, linesCount), &lineOrigins) 505 | 506 | for (idx, lineOrigin) in lineOrigins.enumerated() { 507 | var lineOrigin = lineOrigin 508 | let lineIndex = CFIndex(idx) 509 | let line = unsafeBitCast(CFArrayGetValueAtIndex(lines, lineIndex), to: CTLine.self) 510 | 511 | // Get bounding information of line 512 | var ascent: CGFloat = 0.0 513 | var descent: CGFloat = 0.0 514 | var leading: CGFloat = 0.0 515 | let width = CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, &leading)) 516 | let yMin = CGFloat(floor(lineOrigin.y - descent)) 517 | let yMax = CGFloat(ceil(lineOrigin.y + ascent)) 518 | 519 | // Apply penOffset using flushFactor for horizontal alignment to set lineOrigin since this is the horizontal offset from drawFramesetter 520 | let flushFactor = flushFactorForTextAlignment(textAlignment: textAlignment) 521 | let penOffset = CGFloat(CTLineGetPenOffsetForFlush(line, flushFactor, Double(textRect.size.width))) 522 | lineOrigin.x = penOffset 523 | 524 | // Check if we've already passed the line 525 | if point.y > yMax { 526 | return NSNotFound 527 | } 528 | // Check if the point is within this line vertically 529 | if point.y >= yMin { 530 | // Check if the point is within this line horizontally 531 | if point.x >= lineOrigin.x && point.x <= lineOrigin.x + width { 532 | // Convert CT coordinates to line-relative coordinates 533 | let relativePoint = CGPoint(x: point.x - lineOrigin.x, y: point.y - lineOrigin.y) 534 | return Int(CTLineGetStringIndexForPosition(line, relativePoint)) 535 | } 536 | } 537 | } 538 | 539 | return NSNotFound 540 | } 541 | 542 | private func flushFactorForTextAlignment(textAlignment: NSTextAlignment) -> CGFloat { 543 | switch textAlignment { 544 | case .center: 545 | return 0.5 546 | case .right: 547 | return 1.0 548 | case .left, .natural, .justified: 549 | return 0.0 550 | } 551 | } 552 | } 553 | 554 | 555 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Custom-Class/UIButton-Custom.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton-Custom.swift 3 | // Sportily 4 | // 5 | // Created by Artoon Solutions Private Limited on 26/07/19. 6 | // Copyright © 2019 Artoon Solutions Private Limited. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class UIButton_Custom: UIButton { 12 | 13 | 14 | @IBInspectable var customRadius: CGFloat = 0.0 { 15 | didSet { 16 | self.layer.cornerRadius = self.customRadius 17 | } 18 | } 19 | 20 | @IBInspectable dynamic open var maskBound: Bool = false { 21 | didSet { 22 | self.layer.masksToBounds = maskBound 23 | } 24 | } 25 | 26 | 27 | 28 | @IBInspectable var borderWidth: Double { 29 | get { 30 | return Double(self.layer.borderWidth) 31 | } 32 | set { 33 | self.layer.borderWidth = CGFloat(newValue) 34 | } 35 | } 36 | 37 | 38 | @IBInspectable dynamic open var backdroungColor: UIColor = .gray { 39 | didSet { 40 | self.layer.backgroundColor = backdroungColor.cgColor 41 | } 42 | } 43 | 44 | @IBInspectable var borderColor: UIColor? { 45 | get { 46 | return UIColor(cgColor: self.layer.borderColor!) 47 | } 48 | set { 49 | self.layer.borderColor = newValue?.cgColor 50 | } 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Custom-Class/UIView-CornerRadius.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView-CornerRadius.swift 3 | // AirPlayDemoiPhone 4 | // 5 | // Created by Artoon Solutions Private Limited on 19/07/19. 6 | // Copyright © 2019 Artoon Solutions Private Limited. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @IBDesignable class UIView_CornerRadius: UIView { 12 | 13 | 14 | @IBInspectable var customRadius: CGFloat = 0.0 { 15 | didSet { 16 | self.layer.cornerRadius = self.customRadius 17 | } 18 | } 19 | 20 | @IBInspectable dynamic open var shadowColor: UIColor = .gray { 21 | didSet { 22 | self.layer.shadowColor = shadowColor.cgColor 23 | } 24 | } 25 | 26 | @IBInspectable dynamic open var shadowSize: CGSize = CGSize(width: 0, height: 0) { 27 | didSet { 28 | self.layer.shadowOffset = shadowSize 29 | } 30 | } 31 | 32 | @IBInspectable dynamic open var shadowOpacity: Float = 1.0 { 33 | didSet { 34 | self.layer.shadowOpacity = shadowOpacity 35 | } 36 | } 37 | 38 | @IBInspectable dynamic open var shadowRadius: CGFloat = 0.0 { 39 | didSet { 40 | self.layer.shadowRadius = shadowRadius 41 | } 42 | } 43 | 44 | @IBInspectable dynamic open var maskBound: Bool = false { 45 | didSet { 46 | self.layer.masksToBounds = maskBound 47 | } 48 | } 49 | 50 | @IBInspectable dynamic open var isRoundWidth: Bool = false { 51 | didSet { 52 | if(isRoundWidth) 53 | { 54 | self.layer.cornerRadius = self.frame.width/2 55 | } 56 | else 57 | { 58 | self.layer.cornerRadius = customRadius 59 | } 60 | } 61 | } 62 | 63 | @IBInspectable dynamic open var isRoundHeight: Bool = false { 64 | didSet { 65 | if(isRoundHeight) 66 | { 67 | self.layer.cornerRadius = self.frame.height/2 68 | } 69 | else 70 | { 71 | self.layer.cornerRadius = customRadius 72 | } 73 | } 74 | } 75 | 76 | 77 | 78 | @IBInspectable var borderWidth: Double { 79 | get { 80 | return Double(self.layer.borderWidth) 81 | } 82 | set { 83 | self.layer.borderWidth = CGFloat(newValue) 84 | } 85 | } 86 | 87 | 88 | @IBInspectable var borderColor: UIColor? { 89 | get { 90 | return UIColor(cgColor: self.layer.borderColor!) 91 | } 92 | set { 93 | self.layer.borderColor = newValue?.cgColor 94 | } 95 | } 96 | 97 | 98 | 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Image/btn_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift/Image/btn_back.png -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Image/btn_join.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift/Image/btn_join.png -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Image/icn_date.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift/Image/icn_date.png -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Image/icn_details_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift/Image/icn_details_top.png -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Image/icn_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift/Image/icn_info.png -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Image/icn_no_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift/Image/icn_no_1.png -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Image/icn_top_shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift/Image/icn_top_shadow.png -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/Image/image-placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header-stretching-Animations-Swift/Image/image-placeholder.jpg -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/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 | APPL 17 | CFBundleShortVersionString 18 | 1.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 | -------------------------------------------------------------------------------- /ScrollView-Header-stretching-Animations-Swift/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ScrollView-Header-stretching-Animations-Swift 4 | // 5 | // Created by Artoon Solutions Private Limited on 07/08/19. 6 | // Copyright © 2019 Artoon Solutions Private Limited. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view. 16 | } 17 | 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /ScrollView-Header.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asvaghasiya/ScrollView-Header-stretching-Animations-Swift/09b3e6728d8a4a7d82520226336e11f914fd0591/ScrollView-Header.gif --------------------------------------------------------------------------------