├── LMComposeView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── guozhen.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── guozhen.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── LMComposeView ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── DemoController.h ├── DemoController.m ├── Info.plist ├── LMCollectionView.h ├── LMCollectionView.m ├── LMComposeView │ ├── LMComposeView.h │ ├── LMComposeView.m │ ├── LMSegmentView.h │ ├── LMSegmentView.m │ ├── UIView+LMViewHelper.h │ └── UIView+LMViewHelper.m ├── LMHeaderView.h ├── LMHeaderView.m ├── LMTableView.h ├── LMTableView.m ├── ViewController.h ├── ViewController.m └── main.m ├── LMComposeViewTests ├── Info.plist └── LMComposeViewTests.m ├── LMComposeViewUITests ├── Info.plist └── LMComposeViewUITests.m └── README.md /LMComposeView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0BDB4E542119F2FE00F874FC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E532119F2FE00F874FC /* AppDelegate.m */; }; 11 | 0BDB4E572119F2FE00F874FC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E562119F2FE00F874FC /* ViewController.m */; }; 12 | 0BDB4E5A2119F2FE00F874FC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0BDB4E582119F2FE00F874FC /* Main.storyboard */; }; 13 | 0BDB4E5C2119F2FF00F874FC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0BDB4E5B2119F2FF00F874FC /* Assets.xcassets */; }; 14 | 0BDB4E5F2119F2FF00F874FC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0BDB4E5D2119F2FF00F874FC /* LaunchScreen.storyboard */; }; 15 | 0BDB4E622119F2FF00F874FC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E612119F2FF00F874FC /* main.m */; }; 16 | 0BDB4E6C2119F2FF00F874FC /* LMComposeViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E6B2119F2FF00F874FC /* LMComposeViewTests.m */; }; 17 | 0BDB4E772119F2FF00F874FC /* LMComposeViewUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E762119F2FF00F874FC /* LMComposeViewUITests.m */; }; 18 | 0BDB4E862119F32900F874FC /* DemoController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E852119F32900F874FC /* DemoController.m */; }; 19 | 0BDB4E892119F40100F874FC /* LMComposeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E882119F40100F874FC /* LMComposeView.m */; }; 20 | 0BDB4E8C2119F5BD00F874FC /* LMTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E8B2119F5BD00F874FC /* LMTableView.m */; }; 21 | 0BDB4E8F2119F5E200F874FC /* LMCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E8E2119F5E200F874FC /* LMCollectionView.m */; }; 22 | 0BDB4E95211A009200F874FC /* UIView+LMViewHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BDB4E93211A009100F874FC /* UIView+LMViewHelper.m */; }; 23 | 0BE1DF5E211EF07700D83D13 /* LMSegmentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BE1DF5D211EF07700D83D13 /* LMSegmentView.m */; }; 24 | 0BE1DF622121583F00D83D13 /* LMHeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0BE1DF612121583F00D83D13 /* LMHeaderView.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 0BDB4E682119F2FF00F874FC /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 0BDB4E472119F2FD00F874FC /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 0BDB4E4E2119F2FE00F874FC; 33 | remoteInfo = LMComposeView; 34 | }; 35 | 0BDB4E732119F2FF00F874FC /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 0BDB4E472119F2FD00F874FC /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 0BDB4E4E2119F2FE00F874FC; 40 | remoteInfo = LMComposeView; 41 | }; 42 | /* End PBXContainerItemProxy section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 0BDB4E4F2119F2FE00F874FC /* LMComposeView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LMComposeView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 0BDB4E522119F2FE00F874FC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 47 | 0BDB4E532119F2FE00F874FC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 48 | 0BDB4E552119F2FE00F874FC /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 49 | 0BDB4E562119F2FE00F874FC /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 50 | 0BDB4E592119F2FE00F874FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 51 | 0BDB4E5B2119F2FF00F874FC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 52 | 0BDB4E5E2119F2FF00F874FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 53 | 0BDB4E602119F2FF00F874FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 0BDB4E612119F2FF00F874FC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | 0BDB4E672119F2FF00F874FC /* LMComposeViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LMComposeViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 0BDB4E6B2119F2FF00F874FC /* LMComposeViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMComposeViewTests.m; sourceTree = ""; }; 57 | 0BDB4E6D2119F2FF00F874FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | 0BDB4E722119F2FF00F874FC /* LMComposeViewUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LMComposeViewUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 0BDB4E762119F2FF00F874FC /* LMComposeViewUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMComposeViewUITests.m; sourceTree = ""; }; 60 | 0BDB4E782119F2FF00F874FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | 0BDB4E842119F32900F874FC /* DemoController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoController.h; sourceTree = ""; }; 62 | 0BDB4E852119F32900F874FC /* DemoController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoController.m; sourceTree = ""; }; 63 | 0BDB4E872119F40100F874FC /* LMComposeView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMComposeView.h; sourceTree = ""; }; 64 | 0BDB4E882119F40100F874FC /* LMComposeView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMComposeView.m; sourceTree = ""; }; 65 | 0BDB4E8A2119F5BD00F874FC /* LMTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMTableView.h; sourceTree = ""; }; 66 | 0BDB4E8B2119F5BD00F874FC /* LMTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMTableView.m; sourceTree = ""; }; 67 | 0BDB4E8D2119F5E200F874FC /* LMCollectionView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMCollectionView.h; sourceTree = ""; }; 68 | 0BDB4E8E2119F5E200F874FC /* LMCollectionView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMCollectionView.m; sourceTree = ""; }; 69 | 0BDB4E93211A009100F874FC /* UIView+LMViewHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+LMViewHelper.m"; sourceTree = ""; }; 70 | 0BDB4E94211A009200F874FC /* UIView+LMViewHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+LMViewHelper.h"; sourceTree = ""; }; 71 | 0BE1DF5C211EF07700D83D13 /* LMSegmentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMSegmentView.h; sourceTree = ""; }; 72 | 0BE1DF5D211EF07700D83D13 /* LMSegmentView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMSegmentView.m; sourceTree = ""; }; 73 | 0BE1DF602121583F00D83D13 /* LMHeaderView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LMHeaderView.h; sourceTree = ""; }; 74 | 0BE1DF612121583F00D83D13 /* LMHeaderView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LMHeaderView.m; sourceTree = ""; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | 0BDB4E4C2119F2FE00F874FC /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | 0BDB4E642119F2FF00F874FC /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | 0BDB4E6F2119F2FF00F874FC /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | 0BDB4E462119F2FD00F874FC = { 103 | isa = PBXGroup; 104 | children = ( 105 | 0BDB4E512119F2FE00F874FC /* LMComposeView */, 106 | 0BDB4E6A2119F2FF00F874FC /* LMComposeViewTests */, 107 | 0BDB4E752119F2FF00F874FC /* LMComposeViewUITests */, 108 | 0BDB4E502119F2FE00F874FC /* Products */, 109 | ); 110 | sourceTree = ""; 111 | }; 112 | 0BDB4E502119F2FE00F874FC /* Products */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 0BDB4E4F2119F2FE00F874FC /* LMComposeView.app */, 116 | 0BDB4E672119F2FF00F874FC /* LMComposeViewTests.xctest */, 117 | 0BDB4E722119F2FF00F874FC /* LMComposeViewUITests.xctest */, 118 | ); 119 | name = Products; 120 | sourceTree = ""; 121 | }; 122 | 0BDB4E512119F2FE00F874FC /* LMComposeView */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 0BDB4E522119F2FE00F874FC /* AppDelegate.h */, 126 | 0BDB4E532119F2FE00F874FC /* AppDelegate.m */, 127 | 0BDB4E552119F2FE00F874FC /* ViewController.h */, 128 | 0BDB4E562119F2FE00F874FC /* ViewController.m */, 129 | 0BDB4E842119F32900F874FC /* DemoController.h */, 130 | 0BDB4E852119F32900F874FC /* DemoController.m */, 131 | 0BE1DF5F211EF88400D83D13 /* LMComposeView */, 132 | 0BDB4E8A2119F5BD00F874FC /* LMTableView.h */, 133 | 0BDB4E8B2119F5BD00F874FC /* LMTableView.m */, 134 | 0BDB4E8D2119F5E200F874FC /* LMCollectionView.h */, 135 | 0BDB4E8E2119F5E200F874FC /* LMCollectionView.m */, 136 | 0BE1DF602121583F00D83D13 /* LMHeaderView.h */, 137 | 0BE1DF612121583F00D83D13 /* LMHeaderView.m */, 138 | 0BDB4E582119F2FE00F874FC /* Main.storyboard */, 139 | 0BDB4E5B2119F2FF00F874FC /* Assets.xcassets */, 140 | 0BDB4E5D2119F2FF00F874FC /* LaunchScreen.storyboard */, 141 | 0BDB4E602119F2FF00F874FC /* Info.plist */, 142 | 0BDB4E612119F2FF00F874FC /* main.m */, 143 | ); 144 | path = LMComposeView; 145 | sourceTree = ""; 146 | }; 147 | 0BDB4E6A2119F2FF00F874FC /* LMComposeViewTests */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 0BDB4E6B2119F2FF00F874FC /* LMComposeViewTests.m */, 151 | 0BDB4E6D2119F2FF00F874FC /* Info.plist */, 152 | ); 153 | path = LMComposeViewTests; 154 | sourceTree = ""; 155 | }; 156 | 0BDB4E752119F2FF00F874FC /* LMComposeViewUITests */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 0BDB4E762119F2FF00F874FC /* LMComposeViewUITests.m */, 160 | 0BDB4E782119F2FF00F874FC /* Info.plist */, 161 | ); 162 | path = LMComposeViewUITests; 163 | sourceTree = ""; 164 | }; 165 | 0BE1DF5F211EF88400D83D13 /* LMComposeView */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 0BDB4E94211A009200F874FC /* UIView+LMViewHelper.h */, 169 | 0BDB4E93211A009100F874FC /* UIView+LMViewHelper.m */, 170 | 0BE1DF5C211EF07700D83D13 /* LMSegmentView.h */, 171 | 0BE1DF5D211EF07700D83D13 /* LMSegmentView.m */, 172 | 0BDB4E872119F40100F874FC /* LMComposeView.h */, 173 | 0BDB4E882119F40100F874FC /* LMComposeView.m */, 174 | ); 175 | path = LMComposeView; 176 | sourceTree = ""; 177 | }; 178 | /* End PBXGroup section */ 179 | 180 | /* Begin PBXNativeTarget section */ 181 | 0BDB4E4E2119F2FE00F874FC /* LMComposeView */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = 0BDB4E7B2119F2FF00F874FC /* Build configuration list for PBXNativeTarget "LMComposeView" */; 184 | buildPhases = ( 185 | 0BDB4E4B2119F2FE00F874FC /* Sources */, 186 | 0BDB4E4C2119F2FE00F874FC /* Frameworks */, 187 | 0BDB4E4D2119F2FE00F874FC /* Resources */, 188 | ); 189 | buildRules = ( 190 | ); 191 | dependencies = ( 192 | ); 193 | name = LMComposeView; 194 | productName = LMComposeView; 195 | productReference = 0BDB4E4F2119F2FE00F874FC /* LMComposeView.app */; 196 | productType = "com.apple.product-type.application"; 197 | }; 198 | 0BDB4E662119F2FF00F874FC /* LMComposeViewTests */ = { 199 | isa = PBXNativeTarget; 200 | buildConfigurationList = 0BDB4E7E2119F2FF00F874FC /* Build configuration list for PBXNativeTarget "LMComposeViewTests" */; 201 | buildPhases = ( 202 | 0BDB4E632119F2FF00F874FC /* Sources */, 203 | 0BDB4E642119F2FF00F874FC /* Frameworks */, 204 | 0BDB4E652119F2FF00F874FC /* Resources */, 205 | ); 206 | buildRules = ( 207 | ); 208 | dependencies = ( 209 | 0BDB4E692119F2FF00F874FC /* PBXTargetDependency */, 210 | ); 211 | name = LMComposeViewTests; 212 | productName = LMComposeViewTests; 213 | productReference = 0BDB4E672119F2FF00F874FC /* LMComposeViewTests.xctest */; 214 | productType = "com.apple.product-type.bundle.unit-test"; 215 | }; 216 | 0BDB4E712119F2FF00F874FC /* LMComposeViewUITests */ = { 217 | isa = PBXNativeTarget; 218 | buildConfigurationList = 0BDB4E812119F2FF00F874FC /* Build configuration list for PBXNativeTarget "LMComposeViewUITests" */; 219 | buildPhases = ( 220 | 0BDB4E6E2119F2FF00F874FC /* Sources */, 221 | 0BDB4E6F2119F2FF00F874FC /* Frameworks */, 222 | 0BDB4E702119F2FF00F874FC /* Resources */, 223 | ); 224 | buildRules = ( 225 | ); 226 | dependencies = ( 227 | 0BDB4E742119F2FF00F874FC /* PBXTargetDependency */, 228 | ); 229 | name = LMComposeViewUITests; 230 | productName = LMComposeViewUITests; 231 | productReference = 0BDB4E722119F2FF00F874FC /* LMComposeViewUITests.xctest */; 232 | productType = "com.apple.product-type.bundle.ui-testing"; 233 | }; 234 | /* End PBXNativeTarget section */ 235 | 236 | /* Begin PBXProject section */ 237 | 0BDB4E472119F2FD00F874FC /* Project object */ = { 238 | isa = PBXProject; 239 | attributes = { 240 | LastUpgradeCheck = 0940; 241 | ORGANIZATIONNAME = LiMing; 242 | TargetAttributes = { 243 | 0BDB4E4E2119F2FE00F874FC = { 244 | CreatedOnToolsVersion = 9.4; 245 | }; 246 | 0BDB4E662119F2FF00F874FC = { 247 | CreatedOnToolsVersion = 9.4; 248 | TestTargetID = 0BDB4E4E2119F2FE00F874FC; 249 | }; 250 | 0BDB4E712119F2FF00F874FC = { 251 | CreatedOnToolsVersion = 9.4; 252 | TestTargetID = 0BDB4E4E2119F2FE00F874FC; 253 | }; 254 | }; 255 | }; 256 | buildConfigurationList = 0BDB4E4A2119F2FE00F874FC /* Build configuration list for PBXProject "LMComposeView" */; 257 | compatibilityVersion = "Xcode 9.3"; 258 | developmentRegion = en; 259 | hasScannedForEncodings = 0; 260 | knownRegions = ( 261 | en, 262 | Base, 263 | ); 264 | mainGroup = 0BDB4E462119F2FD00F874FC; 265 | productRefGroup = 0BDB4E502119F2FE00F874FC /* Products */; 266 | projectDirPath = ""; 267 | projectRoot = ""; 268 | targets = ( 269 | 0BDB4E4E2119F2FE00F874FC /* LMComposeView */, 270 | 0BDB4E662119F2FF00F874FC /* LMComposeViewTests */, 271 | 0BDB4E712119F2FF00F874FC /* LMComposeViewUITests */, 272 | ); 273 | }; 274 | /* End PBXProject section */ 275 | 276 | /* Begin PBXResourcesBuildPhase section */ 277 | 0BDB4E4D2119F2FE00F874FC /* Resources */ = { 278 | isa = PBXResourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | 0BDB4E5F2119F2FF00F874FC /* LaunchScreen.storyboard in Resources */, 282 | 0BDB4E5C2119F2FF00F874FC /* Assets.xcassets in Resources */, 283 | 0BDB4E5A2119F2FE00F874FC /* Main.storyboard in Resources */, 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | 0BDB4E652119F2FF00F874FC /* Resources */ = { 288 | isa = PBXResourcesBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | }; 294 | 0BDB4E702119F2FF00F874FC /* Resources */ = { 295 | isa = PBXResourcesBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | ); 299 | runOnlyForDeploymentPostprocessing = 0; 300 | }; 301 | /* End PBXResourcesBuildPhase section */ 302 | 303 | /* Begin PBXSourcesBuildPhase section */ 304 | 0BDB4E4B2119F2FE00F874FC /* Sources */ = { 305 | isa = PBXSourcesBuildPhase; 306 | buildActionMask = 2147483647; 307 | files = ( 308 | 0BDB4E8C2119F5BD00F874FC /* LMTableView.m in Sources */, 309 | 0BDB4E862119F32900F874FC /* DemoController.m in Sources */, 310 | 0BDB4E572119F2FE00F874FC /* ViewController.m in Sources */, 311 | 0BDB4E95211A009200F874FC /* UIView+LMViewHelper.m in Sources */, 312 | 0BDB4E8F2119F5E200F874FC /* LMCollectionView.m in Sources */, 313 | 0BDB4E622119F2FF00F874FC /* main.m in Sources */, 314 | 0BE1DF5E211EF07700D83D13 /* LMSegmentView.m in Sources */, 315 | 0BDB4E892119F40100F874FC /* LMComposeView.m in Sources */, 316 | 0BE1DF622121583F00D83D13 /* LMHeaderView.m in Sources */, 317 | 0BDB4E542119F2FE00F874FC /* AppDelegate.m in Sources */, 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | 0BDB4E632119F2FF00F874FC /* Sources */ = { 322 | isa = PBXSourcesBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | 0BDB4E6C2119F2FF00F874FC /* LMComposeViewTests.m in Sources */, 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | }; 329 | 0BDB4E6E2119F2FF00F874FC /* Sources */ = { 330 | isa = PBXSourcesBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | 0BDB4E772119F2FF00F874FC /* LMComposeViewUITests.m in Sources */, 334 | ); 335 | runOnlyForDeploymentPostprocessing = 0; 336 | }; 337 | /* End PBXSourcesBuildPhase section */ 338 | 339 | /* Begin PBXTargetDependency section */ 340 | 0BDB4E692119F2FF00F874FC /* PBXTargetDependency */ = { 341 | isa = PBXTargetDependency; 342 | target = 0BDB4E4E2119F2FE00F874FC /* LMComposeView */; 343 | targetProxy = 0BDB4E682119F2FF00F874FC /* PBXContainerItemProxy */; 344 | }; 345 | 0BDB4E742119F2FF00F874FC /* PBXTargetDependency */ = { 346 | isa = PBXTargetDependency; 347 | target = 0BDB4E4E2119F2FE00F874FC /* LMComposeView */; 348 | targetProxy = 0BDB4E732119F2FF00F874FC /* PBXContainerItemProxy */; 349 | }; 350 | /* End PBXTargetDependency section */ 351 | 352 | /* Begin PBXVariantGroup section */ 353 | 0BDB4E582119F2FE00F874FC /* Main.storyboard */ = { 354 | isa = PBXVariantGroup; 355 | children = ( 356 | 0BDB4E592119F2FE00F874FC /* Base */, 357 | ); 358 | name = Main.storyboard; 359 | sourceTree = ""; 360 | }; 361 | 0BDB4E5D2119F2FF00F874FC /* LaunchScreen.storyboard */ = { 362 | isa = PBXVariantGroup; 363 | children = ( 364 | 0BDB4E5E2119F2FF00F874FC /* Base */, 365 | ); 366 | name = LaunchScreen.storyboard; 367 | sourceTree = ""; 368 | }; 369 | /* End PBXVariantGroup section */ 370 | 371 | /* Begin XCBuildConfiguration section */ 372 | 0BDB4E792119F2FF00F874FC /* Debug */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ALWAYS_SEARCH_USER_PATHS = NO; 376 | CLANG_ANALYZER_NONNULL = YES; 377 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 378 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 379 | CLANG_CXX_LIBRARY = "libc++"; 380 | CLANG_ENABLE_MODULES = YES; 381 | CLANG_ENABLE_OBJC_ARC = YES; 382 | CLANG_ENABLE_OBJC_WEAK = YES; 383 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 384 | CLANG_WARN_BOOL_CONVERSION = YES; 385 | CLANG_WARN_COMMA = YES; 386 | CLANG_WARN_CONSTANT_CONVERSION = YES; 387 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 388 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 389 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 390 | CLANG_WARN_EMPTY_BODY = YES; 391 | CLANG_WARN_ENUM_CONVERSION = YES; 392 | CLANG_WARN_INFINITE_RECURSION = YES; 393 | CLANG_WARN_INT_CONVERSION = YES; 394 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 395 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 396 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 397 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 398 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 399 | CLANG_WARN_STRICT_PROTOTYPES = YES; 400 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 401 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 402 | CLANG_WARN_UNREACHABLE_CODE = YES; 403 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 404 | CODE_SIGN_IDENTITY = "iPhone Developer"; 405 | COPY_PHASE_STRIP = NO; 406 | DEBUG_INFORMATION_FORMAT = dwarf; 407 | ENABLE_STRICT_OBJC_MSGSEND = YES; 408 | ENABLE_TESTABILITY = YES; 409 | GCC_C_LANGUAGE_STANDARD = gnu11; 410 | GCC_DYNAMIC_NO_PIC = NO; 411 | GCC_NO_COMMON_BLOCKS = YES; 412 | GCC_OPTIMIZATION_LEVEL = 0; 413 | GCC_PREPROCESSOR_DEFINITIONS = ( 414 | "DEBUG=1", 415 | "$(inherited)", 416 | ); 417 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 418 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 419 | GCC_WARN_UNDECLARED_SELECTOR = YES; 420 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 421 | GCC_WARN_UNUSED_FUNCTION = YES; 422 | GCC_WARN_UNUSED_VARIABLE = YES; 423 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 424 | MTL_ENABLE_DEBUG_INFO = YES; 425 | ONLY_ACTIVE_ARCH = YES; 426 | SDKROOT = iphoneos; 427 | }; 428 | name = Debug; 429 | }; 430 | 0BDB4E7A2119F2FF00F874FC /* Release */ = { 431 | isa = XCBuildConfiguration; 432 | buildSettings = { 433 | ALWAYS_SEARCH_USER_PATHS = NO; 434 | CLANG_ANALYZER_NONNULL = YES; 435 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 436 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 437 | CLANG_CXX_LIBRARY = "libc++"; 438 | CLANG_ENABLE_MODULES = YES; 439 | CLANG_ENABLE_OBJC_ARC = YES; 440 | CLANG_ENABLE_OBJC_WEAK = YES; 441 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 442 | CLANG_WARN_BOOL_CONVERSION = YES; 443 | CLANG_WARN_COMMA = YES; 444 | CLANG_WARN_CONSTANT_CONVERSION = YES; 445 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 446 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 447 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 448 | CLANG_WARN_EMPTY_BODY = YES; 449 | CLANG_WARN_ENUM_CONVERSION = YES; 450 | CLANG_WARN_INFINITE_RECURSION = YES; 451 | CLANG_WARN_INT_CONVERSION = YES; 452 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 453 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 454 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 455 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 456 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 457 | CLANG_WARN_STRICT_PROTOTYPES = YES; 458 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 459 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 460 | CLANG_WARN_UNREACHABLE_CODE = YES; 461 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 462 | CODE_SIGN_IDENTITY = "iPhone Developer"; 463 | COPY_PHASE_STRIP = NO; 464 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 465 | ENABLE_NS_ASSERTIONS = NO; 466 | ENABLE_STRICT_OBJC_MSGSEND = YES; 467 | GCC_C_LANGUAGE_STANDARD = gnu11; 468 | GCC_NO_COMMON_BLOCKS = YES; 469 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 470 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 471 | GCC_WARN_UNDECLARED_SELECTOR = YES; 472 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 473 | GCC_WARN_UNUSED_FUNCTION = YES; 474 | GCC_WARN_UNUSED_VARIABLE = YES; 475 | IPHONEOS_DEPLOYMENT_TARGET = 11.4; 476 | MTL_ENABLE_DEBUG_INFO = NO; 477 | SDKROOT = iphoneos; 478 | VALIDATE_PRODUCT = YES; 479 | }; 480 | name = Release; 481 | }; 482 | 0BDB4E7C2119F2FF00F874FC /* Debug */ = { 483 | isa = XCBuildConfiguration; 484 | buildSettings = { 485 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 486 | CODE_SIGN_STYLE = Automatic; 487 | DEVELOPMENT_TEAM = M953KLT6PD; 488 | INFOPLIST_FILE = LMComposeView/Info.plist; 489 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 490 | LD_RUNPATH_SEARCH_PATHS = ( 491 | "$(inherited)", 492 | "@executable_path/Frameworks", 493 | ); 494 | PRODUCT_BUNDLE_IDENTIFIER = Owhat.LMComposeView; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | TARGETED_DEVICE_FAMILY = "1,2"; 497 | }; 498 | name = Debug; 499 | }; 500 | 0BDB4E7D2119F2FF00F874FC /* Release */ = { 501 | isa = XCBuildConfiguration; 502 | buildSettings = { 503 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 504 | CODE_SIGN_STYLE = Automatic; 505 | DEVELOPMENT_TEAM = M953KLT6PD; 506 | INFOPLIST_FILE = LMComposeView/Info.plist; 507 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 508 | LD_RUNPATH_SEARCH_PATHS = ( 509 | "$(inherited)", 510 | "@executable_path/Frameworks", 511 | ); 512 | PRODUCT_BUNDLE_IDENTIFIER = Owhat.LMComposeView; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | TARGETED_DEVICE_FAMILY = "1,2"; 515 | }; 516 | name = Release; 517 | }; 518 | 0BDB4E7F2119F2FF00F874FC /* Debug */ = { 519 | isa = XCBuildConfiguration; 520 | buildSettings = { 521 | BUNDLE_LOADER = "$(TEST_HOST)"; 522 | CODE_SIGN_STYLE = Automatic; 523 | DEVELOPMENT_TEAM = M953KLT6PD; 524 | INFOPLIST_FILE = LMComposeViewTests/Info.plist; 525 | LD_RUNPATH_SEARCH_PATHS = ( 526 | "$(inherited)", 527 | "@executable_path/Frameworks", 528 | "@loader_path/Frameworks", 529 | ); 530 | PRODUCT_BUNDLE_IDENTIFIER = Owhat.LMComposeViewTests; 531 | PRODUCT_NAME = "$(TARGET_NAME)"; 532 | TARGETED_DEVICE_FAMILY = "1,2"; 533 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LMComposeView.app/LMComposeView"; 534 | }; 535 | name = Debug; 536 | }; 537 | 0BDB4E802119F2FF00F874FC /* Release */ = { 538 | isa = XCBuildConfiguration; 539 | buildSettings = { 540 | BUNDLE_LOADER = "$(TEST_HOST)"; 541 | CODE_SIGN_STYLE = Automatic; 542 | DEVELOPMENT_TEAM = M953KLT6PD; 543 | INFOPLIST_FILE = LMComposeViewTests/Info.plist; 544 | LD_RUNPATH_SEARCH_PATHS = ( 545 | "$(inherited)", 546 | "@executable_path/Frameworks", 547 | "@loader_path/Frameworks", 548 | ); 549 | PRODUCT_BUNDLE_IDENTIFIER = Owhat.LMComposeViewTests; 550 | PRODUCT_NAME = "$(TARGET_NAME)"; 551 | TARGETED_DEVICE_FAMILY = "1,2"; 552 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LMComposeView.app/LMComposeView"; 553 | }; 554 | name = Release; 555 | }; 556 | 0BDB4E822119F2FF00F874FC /* Debug */ = { 557 | isa = XCBuildConfiguration; 558 | buildSettings = { 559 | CODE_SIGN_STYLE = Automatic; 560 | DEVELOPMENT_TEAM = M953KLT6PD; 561 | INFOPLIST_FILE = LMComposeViewUITests/Info.plist; 562 | LD_RUNPATH_SEARCH_PATHS = ( 563 | "$(inherited)", 564 | "@executable_path/Frameworks", 565 | "@loader_path/Frameworks", 566 | ); 567 | PRODUCT_BUNDLE_IDENTIFIER = Owhat.LMComposeViewUITests; 568 | PRODUCT_NAME = "$(TARGET_NAME)"; 569 | TARGETED_DEVICE_FAMILY = "1,2"; 570 | TEST_TARGET_NAME = LMComposeView; 571 | }; 572 | name = Debug; 573 | }; 574 | 0BDB4E832119F2FF00F874FC /* Release */ = { 575 | isa = XCBuildConfiguration; 576 | buildSettings = { 577 | CODE_SIGN_STYLE = Automatic; 578 | DEVELOPMENT_TEAM = M953KLT6PD; 579 | INFOPLIST_FILE = LMComposeViewUITests/Info.plist; 580 | LD_RUNPATH_SEARCH_PATHS = ( 581 | "$(inherited)", 582 | "@executable_path/Frameworks", 583 | "@loader_path/Frameworks", 584 | ); 585 | PRODUCT_BUNDLE_IDENTIFIER = Owhat.LMComposeViewUITests; 586 | PRODUCT_NAME = "$(TARGET_NAME)"; 587 | TARGETED_DEVICE_FAMILY = "1,2"; 588 | TEST_TARGET_NAME = LMComposeView; 589 | }; 590 | name = Release; 591 | }; 592 | /* End XCBuildConfiguration section */ 593 | 594 | /* Begin XCConfigurationList section */ 595 | 0BDB4E4A2119F2FE00F874FC /* Build configuration list for PBXProject "LMComposeView" */ = { 596 | isa = XCConfigurationList; 597 | buildConfigurations = ( 598 | 0BDB4E792119F2FF00F874FC /* Debug */, 599 | 0BDB4E7A2119F2FF00F874FC /* Release */, 600 | ); 601 | defaultConfigurationIsVisible = 0; 602 | defaultConfigurationName = Release; 603 | }; 604 | 0BDB4E7B2119F2FF00F874FC /* Build configuration list for PBXNativeTarget "LMComposeView" */ = { 605 | isa = XCConfigurationList; 606 | buildConfigurations = ( 607 | 0BDB4E7C2119F2FF00F874FC /* Debug */, 608 | 0BDB4E7D2119F2FF00F874FC /* Release */, 609 | ); 610 | defaultConfigurationIsVisible = 0; 611 | defaultConfigurationName = Release; 612 | }; 613 | 0BDB4E7E2119F2FF00F874FC /* Build configuration list for PBXNativeTarget "LMComposeViewTests" */ = { 614 | isa = XCConfigurationList; 615 | buildConfigurations = ( 616 | 0BDB4E7F2119F2FF00F874FC /* Debug */, 617 | 0BDB4E802119F2FF00F874FC /* Release */, 618 | ); 619 | defaultConfigurationIsVisible = 0; 620 | defaultConfigurationName = Release; 621 | }; 622 | 0BDB4E812119F2FF00F874FC /* Build configuration list for PBXNativeTarget "LMComposeViewUITests" */ = { 623 | isa = XCConfigurationList; 624 | buildConfigurations = ( 625 | 0BDB4E822119F2FF00F874FC /* Debug */, 626 | 0BDB4E832119F2FF00F874FC /* Release */, 627 | ); 628 | defaultConfigurationIsVisible = 0; 629 | defaultConfigurationName = Release; 630 | }; 631 | /* End XCConfigurationList section */ 632 | }; 633 | rootObject = 0BDB4E472119F2FD00F874FC /* Project object */; 634 | } 635 | -------------------------------------------------------------------------------- /LMComposeView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LMComposeView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LMComposeView.xcodeproj/project.xcworkspace/xcuserdata/guozhen.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeesimEverglow/LMComposeView/0d51dc1b17925c23c83aa5ec212351b82fb1e550/LMComposeView.xcodeproj/project.xcworkspace/xcuserdata/guozhen.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /LMComposeView.xcodeproj/xcuserdata/guozhen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LMComposeView.xcodeproj/xcuserdata/guozhen.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | LMComposeView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LMComposeView/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /LMComposeView/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | [self adaptateIOS11]; 21 | return YES; 22 | } 23 | 24 | 25 | - (void)applicationWillResignActive:(UIApplication *)application { 26 | // 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. 27 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 28 | } 29 | 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application { 32 | // 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. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application { 43 | // 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. 44 | } 45 | 46 | 47 | - (void)applicationWillTerminate:(UIApplication *)application { 48 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 49 | } 50 | 51 | #pragma mark - 适配iOS11以上 scrollview 乱序滚动问题 52 | - (void)adaptateIOS11{ 53 | //适配iOS11以上UITableview 、UICollectionView、UIScrollview 列表/页面偏移 54 | if (@available(iOS 11.0, *)){ 55 | [[UIScrollView appearance] setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever]; 56 | 57 | [[UITableView appearance] setEstimatedRowHeight:0]; 58 | [[UITableView appearance] setEstimatedSectionFooterHeight:0]; 59 | [[UITableView appearance] setEstimatedSectionHeaderHeight:0]; 60 | 61 | } 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /LMComposeView/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 | } -------------------------------------------------------------------------------- /LMComposeView/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /LMComposeView/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 | -------------------------------------------------------------------------------- /LMComposeView/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /LMComposeView/DemoController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoController.h 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DemoController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /LMComposeView/DemoController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoController.m 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import "DemoController.h" 10 | #import "LMComposeView.h" 11 | #import "LMTableView.h" 12 | #import "LMCollectionView.h" 13 | #import "LMHeaderView.h" 14 | #import "UIView+LMViewHelper.h" 15 | 16 | 17 | @interface DemoController () 18 | 19 | @property(nonatomic,strong) LMComposeView * composeView; 20 | 21 | @property (nonatomic,strong) LMHeaderView * headerView; 22 | 23 | @end 24 | 25 | @implementation DemoController 26 | 27 | - (void)viewDidLoad { 28 | [super viewDidLoad]; 29 | // Do any additional setup after loading the view. 30 | self.view.backgroundColor = [UIColor whiteColor]; 31 | 32 | NSMutableArray * scrollViewArray = [[NSMutableArray alloc]init]; 33 | NSMutableArray * titleArray = [[NSMutableArray alloc]init]; 34 | for (int i = 0; i<6; i++) { 35 | int x = i%2; 36 | if (x == 1) { 37 | LMTableView * tableView = [[LMTableView alloc]init]; 38 | tableView.dataSourceCount = 20; 39 | [scrollViewArray addObject:tableView]; 40 | }else{ 41 | LMCollectionView * collectionView = [[LMCollectionView alloc]init]; 42 | collectionView.dataSourceCount = 30; 43 | [scrollViewArray addObject:collectionView]; 44 | } 45 | 46 | [titleArray addObject:[NSString stringWithFormat:@"滚动视图%d",i]]; 47 | } 48 | 49 | [self.composeView confirmComposeViewWithScrollViewArray:scrollViewArray withSegmentButtonTitleArray:titleArray withHeaderView:self.headerView withComposeViewFrame:CGRectMake(0, 64,self.view.width, self.view.height-64)]; 50 | 51 | } 52 | 53 | -(void)composeViewDidClickSegementButtonWithIndex:(NSInteger)index{ 54 | 55 | NSLog(@"---滚动到了%ld---",(long)index); 56 | } 57 | 58 | 59 | -(LMComposeView *)composeView{ 60 | if (!_composeView) { 61 | _composeView = [[LMComposeView alloc]init]; 62 | _composeView.delegate = self; 63 | [self.view addSubview:_composeView]; 64 | } 65 | return _composeView; 66 | } 67 | 68 | /** 69 | 如果想要 headerview响应事件 则需要自定义一个HeaderView 70 | 并且重写 71 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 72 | 判断后返回需要触发响应的view 73 | */ 74 | -(LMHeaderView *)headerView{ 75 | if (!_headerView) { 76 | _headerView = [[LMHeaderView alloc]init]; 77 | 78 | _headerView.frame = CGRectMake(0, 0,[UIScreen mainScreen].bounds.size.width, 200); 79 | } 80 | return _headerView; 81 | } 82 | 83 | 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /LMComposeView/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 | -------------------------------------------------------------------------------- /LMComposeView/LMCollectionView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMCollectionView.h 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMCollectionView : UICollectionView 12 | 13 | @property (nonatomic,assign) int dataSourceCount; 14 | 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /LMComposeView/LMCollectionView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMCollectionView.m 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import "LMCollectionView.h" 10 | 11 | #define collectionID @"collectionview" 12 | 13 | @interface LMCollectionView () 14 | 15 | 16 | @end 17 | 18 | @implementation LMCollectionView 19 | 20 | - (instancetype)init 21 | { 22 | UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; 23 | layout.itemSize = CGSizeMake(100, 100); 24 | layout.minimumInteritemSpacing = 10.0f; 25 | layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10); 26 | layout.scrollDirection = UICollectionViewScrollDirectionVertical; 27 | 28 | self = [super initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 10) collectionViewLayout:layout]; 29 | if (self) { 30 | self.backgroundColor = [UIColor clearColor]; 31 | //注册cell 32 | [self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:collectionID]; 33 | 34 | self.delegate = self; 35 | self.dataSource = self; 36 | self.showsVerticalScrollIndicator = NO; 37 | 38 | self.dataSourceCount = 10; 39 | } 40 | return self; 41 | } 42 | 43 | -(void)setDataSourceCount:(int)dataSourceCount{ 44 | _dataSourceCount = dataSourceCount; 45 | 46 | [self reloadData]; 47 | } 48 | 49 | #pragma mark - UICollectionViewDataSource 50 | 51 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 52 | { 53 | return self.dataSourceCount; 54 | } 55 | 56 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 57 | { 58 | UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:collectionID forIndexPath:indexPath]; 59 | cell.backgroundColor = [UIColor redColor]; 60 | return cell; 61 | } 62 | 63 | 64 | 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /LMComposeView/LMComposeView/LMComposeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMComposeView.h 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol LMComposeViewDelegate 12 | 13 | -(void)composeViewDidClickSegementButtonWithIndex:(NSInteger)index; 14 | 15 | @end 16 | 17 | @interface LMComposeView : UIView 18 | 19 | @property (nonatomic,weak) id delegate; 20 | 21 | 22 | /** 23 | 布局复合View 24 | 25 | @param scrollViewArray 滚动视图的数组 26 | @param titleArray 分类标题字符串数组 27 | @param headerView 顶部公用的headerView 28 | @param composeViewFrame 组合空间的frame 29 | */ 30 | -(void)confirmComposeViewWithScrollViewArray:(NSArray *)scrollViewArray withSegmentButtonTitleArray:(NSArray*)titleArray withHeaderView:(UIView *)headerView withComposeViewFrame:(CGRect)composeViewFrame; 31 | 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /LMComposeView/LMComposeView/LMComposeView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMComposeView.m 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import "LMComposeView.h" 10 | #import "LMSegmentView.h" 11 | #import "UIView+LMViewHelper.h" 12 | 13 | #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width 14 | #define HEAD_HEIGHT (self.headerView.height +self.segmentView.height) 15 | 16 | @interface LMComposeView () 17 | 18 | /** 19 | 自定义的headerView 20 | */ 21 | @property (nonatomic,strong) UIView * headerView; 22 | 23 | /** 24 | 添加底部的滚动视图的数据 25 | */ 26 | @property (nonatomic,strong) NSArray * scrollViewArray; 27 | 28 | /** 29 | 滚动视图的标题数据源 30 | */ 31 | @property (nonatomic,strong) NSArray * titleArray; 32 | /** 33 | 最底层的scrollView 34 | */ 35 | @property (nonatomic,strong) UIScrollView * backScrollView; 36 | /** 37 | 分类查询自定义view 38 | */ 39 | @property (nonatomic,strong) LMSegmentView * segmentView; 40 | /** 41 | 当前选中位置 42 | */ 43 | @property (nonatomic,assign) NSInteger currentIndex; 44 | 45 | @end 46 | 47 | @implementation LMComposeView 48 | 49 | -(void)dealloc{ 50 | 51 | for (UIScrollView * scrollView in self.scrollViewArray) { 52 | [scrollView removeObserver:self forKeyPath:@"contentOffset"]; 53 | } 54 | 55 | } 56 | 57 | -(void)confirmComposeViewWithScrollViewArray:(NSArray *)scrollViewArray withSegmentButtonTitleArray:(NSArray*)titleArray withHeaderView:(UIView *)headerView withComposeViewFrame:(CGRect)composeViewFrame{ 58 | 59 | NSAssert(scrollViewArray.count==titleArray.count, @"滚动视图的数据数量不等于标题数,请检查"); 60 | 61 | self.scrollViewArray = scrollViewArray; 62 | self.titleArray = titleArray; 63 | self.headerView = headerView; 64 | self.frame = composeViewFrame; 65 | 66 | [self confirmUI]; 67 | 68 | } 69 | 70 | 71 | -(void)confirmUI{ 72 | //默认选中1 73 | self.currentIndex = 0; 74 | [self.backScrollView class]; 75 | [self addSubview:self.headerView]; 76 | [self addSubview:self.segmentView]; 77 | 78 | __weak typeof(self) weakSelf = self; 79 | 80 | [self.scrollViewArray enumerateObjectsUsingBlock:^(UIScrollView * scrollView, NSUInteger idx, BOOL * _Nonnull stop) { 81 | 82 | scrollView.tag = 9000+idx; 83 | scrollView.frame = CGRectMake(SCREEN_WIDTH*idx, 0, weakSelf.width, weakSelf.height); 84 | [weakSelf.backScrollView addSubview:scrollView]; 85 | 86 | if ([scrollView isKindOfClass:[UITableView class]]) { 87 | UITableView * tableView = (UITableView *)scrollView; 88 | if (tableView.tableHeaderView) { 89 | UIView * headerView = tableView.tableHeaderView; 90 | headerView.frame = (CGRect){0, 0, SCREEN_WIDTH, HEAD_HEIGHT}; 91 | tableView.tableHeaderView = headerView; 92 | }else{ 93 | UIView *headerView = [[UIView alloc] initWithFrame:(CGRect){0, 0, SCREEN_WIDTH, HEAD_HEIGHT}]; 94 | tableView.tableHeaderView = headerView; 95 | } 96 | }else if ([scrollView isKindOfClass:[UICollectionView class]]){ 97 | UICollectionView * collectionView = (UICollectionView *)scrollView; 98 | [collectionView.collectionViewLayout setValue:[NSValue valueWithUIEdgeInsets:[weakSelf getFixCollectionViewLayoutInsetWithInsetString:[NSString stringWithFormat:@"%@",[collectionView.collectionViewLayout valueForKey:@"sectionInset"]]]] forKey:@"sectionInset"]; 99 | } 100 | 101 | [scrollView addObserver:weakSelf forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionInitial context:nil]; 102 | }]; 103 | } 104 | 105 | #pragma mark - KVO监听滚动的offset的变化 106 | 107 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 108 | { 109 | if ([object tag]%9000!=self.self.currentIndex) return; 110 | if ([keyPath isEqualToString:@"contentOffset"]) { 111 | UIScrollView *scrollView = object; 112 | CGFloat contentOffsetY = scrollView.contentOffset.y; 113 | // 如果滑动没有超过临界值 114 | if (contentOffsetY < self.headerView.height) { 115 | // 让这几个个tableView的偏移量相等 116 | for (UIScrollView * allscrollView in self.scrollViewArray) { 117 | if (allscrollView.contentOffset.y != scrollView.contentOffset.y) { 118 | allscrollView.contentOffset = scrollView.contentOffset; 119 | } 120 | } 121 | //动态修改y值 122 | self.headerView.y = -contentOffsetY; 123 | // 一旦大于等于临界值点了,让headerView的y值等于临界值点,就停留在上边了 124 | self.segmentView.y = self.headerView.height-contentOffsetY; 125 | 126 | } 127 | else if (contentOffsetY >= self.headerView.height) { 128 | self.headerView.y = -self.headerView.height; 129 | self.segmentView.y = 0; 130 | 131 | } 132 | 133 | [self reloadMaxOffsetY]; 134 | } 135 | } 136 | 137 | -(UIEdgeInsets)getFixCollectionViewLayoutInsetWithInsetString:(NSString *)collectionViewInsetString{ 138 | NSRange rightParenthesesRange = [collectionViewInsetString rangeOfString:@"{"]; 139 | if (rightParenthesesRange.location == NSNotFound) return UIEdgeInsetsMake(0, 0, 0, 0); 140 | NSRange leftParenthesesRange = [collectionViewInsetString rangeOfString:@"}"]; 141 | NSString * tempString = [collectionViewInsetString substringWithRange:NSMakeRange(rightParenthesesRange.location, leftParenthesesRange.location-rightParenthesesRange.location+1)]; 142 | UIEdgeInsets collectionSectionInset = UIEdgeInsetsFromString(tempString); 143 | return UIEdgeInsetsMake(collectionSectionInset.top+HEAD_HEIGHT, collectionSectionInset.left, collectionSectionInset.bottom, collectionSectionInset.right); 144 | } 145 | 146 | 147 | #pragma mark - scrollView Delegate 148 | 149 | -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ 150 | NSInteger index = scrollView.contentOffset.x/SCREEN_WIDTH; 151 | self.currentIndex = index; 152 | self.segmentView.scrollViewToSegmentIndex = index; 153 | [self reloadMaxOffsetY]; 154 | if ([self.delegate respondsToSelector:@selector(composeViewDidClickSegementButtonWithIndex:)]) { 155 | [self.delegate composeViewDidClickSegementButtonWithIndex:index]; 156 | } 157 | } 158 | 159 | 160 | // 刷新最大OffsetY,让三个tableView同步 161 | - (void)reloadMaxOffsetY { 162 | //计算出最大偏移量 163 | CGFloat maxOffsetY = 0; 164 | for (UIScrollView *scrollView in self.scrollViewArray) { 165 | if (scrollView.contentOffset.y > maxOffsetY) { 166 | maxOffsetY = scrollView.contentOffset.y; 167 | } 168 | } 169 | // 如果最大偏移量大于顶部headerView的高度,处理下每个tableView的偏移量 170 | if (maxOffsetY > self.headerView.height) { 171 | for (UIScrollView * scrollView in self.scrollViewArray) { 172 | if (scrollView.contentOffset.y < self.headerView.height) { 173 | scrollView.contentOffset = CGPointMake(0, self.headerView.height); 174 | } 175 | } 176 | } 177 | } 178 | 179 | 180 | -(UIScrollView *)backScrollView{ 181 | if (!_backScrollView) { 182 | _backScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, self.height)]; 183 | _backScrollView.delegate = self; 184 | _backScrollView.contentSize = CGSizeMake(self.scrollViewArray.count*SCREEN_WIDTH, self.height); 185 | _backScrollView.pagingEnabled = YES; 186 | _backScrollView.bounces = NO; 187 | _backScrollView.showsVerticalScrollIndicator = NO; 188 | _backScrollView.showsHorizontalScrollIndicator = NO; 189 | [self addSubview:_backScrollView]; 190 | } 191 | return _backScrollView; 192 | } 193 | 194 | -(LMSegmentView *)segmentView{ 195 | if (!_segmentView) { 196 | _segmentView = [[LMSegmentView alloc]initWithTitleArray:self.titleArray withFrame:CGRectMake(0, self.headerView.height, SCREEN_WIDTH, 40)]; 197 | __weak typeof(self) weakSelf = self; 198 | //点击到第几个分类 199 | [_segmentView setDidSelectSegmentWithIndexBlock:^(NSInteger index) { 200 | weakSelf.currentIndex = index; 201 | [weakSelf reloadMaxOffsetY]; 202 | [weakSelf.backScrollView setContentOffset:CGPointMake(SCREEN_WIDTH*index, 0) animated:YES]; 203 | if ([weakSelf.delegate respondsToSelector:@selector(composeViewDidClickSegementButtonWithIndex:)]) { 204 | [weakSelf.delegate composeViewDidClickSegementButtonWithIndex:index]; 205 | } 206 | }]; 207 | } 208 | return _segmentView; 209 | } 210 | 211 | 212 | @end 213 | -------------------------------------------------------------------------------- /LMComposeView/LMComposeView/LMSegmentView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMSegmentView.h 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/11. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMSegmentView : UIScrollView 12 | 13 | /** 14 | 初始化 15 | 16 | @param titleArray 每个分组的标题 17 | @param frame 坐标 18 | @return 返回segment 19 | */ 20 | - (instancetype)initWithTitleArray:(NSArray *)titleArray withFrame:(CGRect)frame; 21 | 22 | /** 23 | 点击每个分类按钮的代理 24 | */ 25 | @property (nonatomic,copy) void (^didSelectSegmentWithIndexBlock)(NSInteger index); 26 | 27 | /** 28 | 直接滚动到某个分类 29 | */ 30 | @property (nonatomic,assign) NSInteger scrollViewToSegmentIndex; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /LMComposeView/LMComposeView/LMSegmentView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMSegmentView.m 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/11. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import "LMSegmentView.h" 10 | 11 | #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width 12 | 13 | @interface LMSegmentView () 14 | 15 | @property (nonatomic,strong) NSArray * titleArray; 16 | 17 | @property (nonatomic,strong) UIView * selectView; 18 | 19 | @end 20 | 21 | @implementation LMSegmentView 22 | 23 | - (instancetype)initWithTitleArray:(NSArray *)titleArray withFrame:(CGRect)frame 24 | { 25 | self = [super init]; 26 | if (self) { 27 | self.backgroundColor = [UIColor whiteColor]; 28 | self.showsVerticalScrollIndicator = NO; 29 | self.showsHorizontalScrollIndicator = NO; 30 | self.frame = frame; 31 | self.titleArray = titleArray; 32 | 33 | 34 | [self confirmUI]; 35 | 36 | } 37 | return self; 38 | } 39 | 40 | -(void)confirmUI{ 41 | 42 | CGFloat width = SCREEN_WIDTH/self.titleArray.count; 43 | 44 | if (width maxOffsetX) offsetX = maxOffsetX; 90 | // 滚动标题滚动条 91 | [self setContentOffset:CGPointMake(offsetX, 0) animated:YES]; 92 | } 93 | 94 | 95 | -(UIView *)selectView{ 96 | if (!_selectView) { 97 | _selectView = [[UIView alloc]init]; 98 | _selectView.backgroundColor = [UIColor purpleColor]; 99 | [self addSubview:_selectView]; 100 | } 101 | return _selectView; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /LMComposeView/LMComposeView/UIView+LMViewHelper.h: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // UIView+LMViewHelper.h 4 | // Owhat 5 | // 6 | // Created by Leesim on 2018/5/1. 7 | // Copyright © 2018年 Owhat. All rights reserved. 8 | // 9 | 10 | 11 | #import 12 | 13 | @interface UIView (LMViewHelper) 14 | @property (nonatomic, assign)CGFloat x; 15 | @property (nonatomic, assign)CGFloat y; 16 | @property (nonatomic, assign)CGFloat width; 17 | @property (nonatomic, assign)CGFloat height; 18 | @property (nonatomic, assign)CGFloat centerX; 19 | @property (nonatomic, assign)CGFloat centerY; 20 | @property (nonatomic, assign)CGSize size; 21 | @property(nonatomic, assign) IBInspectable CGFloat borderWidth; 22 | @property(nonatomic, assign) IBInspectable UIColor *borderColor; 23 | @property(nonatomic, assign) IBInspectable CGFloat cornerRadius; 24 | 25 | @property (nonatomic, assign)CGFloat max_x; 26 | @property (nonatomic, assign)CGFloat max_y; 27 | @property (nonatomic, assign)CGFloat min_x; 28 | @property (nonatomic, assign)CGFloat min_y; 29 | /** 30 | * 水平居中 31 | */ 32 | - (void)alignHorizontal; 33 | /** 34 | * 垂直居中 35 | */ 36 | - (void)alignVertical; 37 | 38 | /** 39 | * 判断是否显示在主窗口上面 40 | * 41 | * @return 是否 42 | */ 43 | - (BOOL)isShowOnWindow; 44 | 45 | 46 | 47 | /** 48 | * 设置部分圆角(绝对布局) 49 | * 50 | * @param corners 需要设置为圆角的角 UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerAllCorners 51 | * @param radii 需要设置的圆角大小 例如 CGSizeMake(20.0f, 20.0f) 52 | */ 53 | - (void)addRoundedCorners:(UIRectCorner)corners 54 | withRadii:(CGSize)radii; 55 | /** 56 | * 设置部分圆角(相对布局) 57 | * 58 | * @param corners 需要设置为圆角的角 UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerAllCorners 59 | * @param radii 需要设置的圆角大小 例如 CGSizeMake(20.0f, 20.0f) 60 | * @param rect 需要设置的圆角view的rect 61 | */ 62 | - (void)addRoundedCorners:(UIRectCorner)corners 63 | withRadii:(CGSize)radii 64 | viewRect:(CGRect)rect; 65 | 66 | 67 | - (UIViewController *)getCurrentController; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /LMComposeView/LMComposeView/UIView+LMViewHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LMViewHelper.m 3 | // Owhat 4 | // 5 | // Created by Leesim on 2018/5/1. 6 | // Copyright © 2018年 Owhat. All rights reserved. 7 | // 8 | 9 | #import "UIView+LMViewHelper.h" 10 | 11 | @implementation UIView (LMViewHelper) 12 | 13 | - (void)setX:(CGFloat)x 14 | { 15 | CGRect frame = self.frame; 16 | frame.origin.x = x; 17 | self.frame = frame; 18 | } 19 | 20 | - (CGFloat)x 21 | { 22 | return self.frame.origin.x; 23 | } 24 | 25 | - (void)setY:(CGFloat)y 26 | { 27 | CGRect frame = self.frame; 28 | frame.origin.y = y; 29 | self.frame = frame; 30 | } 31 | 32 | - (CGFloat)y 33 | { 34 | return self.frame.origin.y; 35 | } 36 | 37 | -(CGFloat)max_x{ 38 | return CGRectGetMaxX(self.frame); 39 | } 40 | -(CGFloat)max_y{ 41 | return CGRectGetMaxY(self.frame); 42 | } 43 | -(CGFloat)min_x{ 44 | return CGRectGetMinX(self.frame); 45 | } 46 | -(CGFloat)min_y{ 47 | return CGRectGetMinY(self.frame); 48 | } 49 | 50 | - (void)setWidth:(CGFloat)width 51 | { 52 | CGRect frame = self.frame; 53 | frame.size.width = width; 54 | self.frame = frame; 55 | } 56 | 57 | - (CGFloat)width 58 | { 59 | return self.frame.size.width; 60 | } 61 | 62 | - (void)setHeight:(CGFloat)height 63 | { 64 | CGRect frame = self.frame; 65 | frame.size.height = height; 66 | self.frame= frame; 67 | } 68 | 69 | - (CGFloat)height 70 | { 71 | return self.frame.size.height; 72 | } 73 | 74 | - (void)setSize:(CGSize)size 75 | { 76 | CGRect frame = self.frame; 77 | frame.size = size; 78 | self.frame = frame; 79 | } 80 | 81 | - (CGSize)size 82 | { 83 | return self.frame.size; 84 | } 85 | 86 | - (void)setCenterX:(CGFloat)centerX 87 | { 88 | CGPoint center = self.center; 89 | center.x = centerX; 90 | self.center = center; 91 | } 92 | 93 | - (CGFloat)centerX 94 | { 95 | return self.center.x; 96 | } 97 | 98 | - (void)setCenterY:(CGFloat)centerY 99 | { 100 | CGPoint center = self.center; 101 | center.y = centerY; 102 | self.center = center; 103 | } 104 | 105 | - (CGFloat)centerY 106 | { 107 | return self.center.y; 108 | } 109 | - (void)alignHorizontal 110 | { 111 | self.x = (self.superview.width - self.width) * 0.5; 112 | } 113 | 114 | - (void)alignVertical 115 | { 116 | self.y = (self.superview.height - self.height) *0.5; 117 | } 118 | 119 | - (void)setBorderWidth:(CGFloat)borderWidth 120 | { 121 | 122 | if (borderWidth < 0) { 123 | return; 124 | } 125 | self.layer.borderWidth = borderWidth; 126 | } 127 | 128 | - (void)setBorderColor:(UIColor *)borderColor 129 | { 130 | self.layer.borderColor = borderColor.CGColor; 131 | } 132 | 133 | - (void)setCornerRadius:(CGFloat)cornerRadius 134 | { 135 | self.layer.cornerRadius = cornerRadius; 136 | self.layer.masksToBounds = YES; 137 | } 138 | 139 | - (BOOL)isShowOnWindow 140 | { 141 | //主窗口 142 | UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow; 143 | 144 | //相对于父控件转换之后的rect 145 | CGRect newRect = [keyWindow convertRect:self.frame fromView:self.superview]; 146 | //主窗口的bounds 147 | CGRect winBounds = keyWindow.bounds; 148 | //判断两个坐标系是否有交汇的地方,返回bool值 149 | BOOL isIntersects = CGRectIntersectsRect(newRect, winBounds); 150 | if (self.hidden != YES && self.alpha >0.01 && self.window == keyWindow && isIntersects) { 151 | return YES; 152 | }else{ 153 | return NO; 154 | } 155 | } 156 | 157 | - (CGFloat)borderWidth 158 | { 159 | return self.borderWidth; 160 | } 161 | 162 | - (UIColor *)borderColor 163 | { 164 | return self.borderColor; 165 | 166 | } 167 | 168 | - (CGFloat)cornerRadius 169 | { 170 | return self.cornerRadius; 171 | } 172 | 173 | /** 174 | * 设置部分圆角(绝对布局) 175 | * 176 | * @param corners 需要设置为圆角的角 UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerAllCorners 177 | * @param radii 需要设置的圆角大小 例如 CGSizeMake(20.0f, 20.0f) 178 | */ 179 | - (void)addRoundedCorners:(UIRectCorner)corners 180 | withRadii:(CGSize)radii { 181 | 182 | UIBezierPath* rounded = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:radii]; 183 | CAShapeLayer* shape = [[CAShapeLayer alloc] init]; 184 | [shape setPath:rounded.CGPath]; 185 | 186 | self.layer.mask = shape; 187 | } 188 | 189 | /** 190 | * 设置部分圆角(相对布局) 191 | * 192 | * @param corners 需要设置为圆角的角 UIRectCornerTopLeft | UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerAllCorners 193 | * @param radii 需要设置的圆角大小 例如 CGSizeMake(20.0f, 20.0f) 194 | * @param rect 需要设置的圆角view的rect 195 | */ 196 | - (void)addRoundedCorners:(UIRectCorner)corners 197 | withRadii:(CGSize)radii 198 | viewRect:(CGRect)rect { 199 | 200 | UIBezierPath* rounded = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:radii]; 201 | CAShapeLayer* shape = [[CAShapeLayer alloc] init]; 202 | [shape setPath:rounded.CGPath]; 203 | 204 | self.layer.mask = shape; 205 | } 206 | 207 | - (UIViewController *)getCurrentController 208 | { 209 | UIResponder *responder = [self nextResponder]; 210 | while (responder) { 211 | if ([responder isKindOfClass:[UIViewController class]]) { 212 | return (UIViewController *)responder; 213 | } 214 | responder = [responder nextResponder]; 215 | } 216 | return nil; 217 | } 218 | 219 | 220 | @end 221 | -------------------------------------------------------------------------------- /LMComposeView/LMHeaderView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMHeaderView.h 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/13. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMHeaderView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /LMComposeView/LMHeaderView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMHeaderView.m 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/13. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import "LMHeaderView.h" 10 | 11 | 12 | @interface LMHeaderView () 13 | 14 | @property (nonatomic,strong) UIButton * titleButton; 15 | 16 | @end 17 | 18 | @implementation LMHeaderView 19 | 20 | - (void)layoutSubviews{ 21 | 22 | self.backgroundColor = [UIColor blueColor]; 23 | self.titleButton.center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2); 24 | 25 | } 26 | 27 | -(void)titleButtonAction:(UIButton*)button{ 28 | button.selected = !button.selected; 29 | } 30 | 31 | -(UIButton *)titleButton{ 32 | if (!_titleButton) { 33 | _titleButton = [[UIButton alloc]init]; 34 | _titleButton.bounds = CGRectMake(0, 0, 100, 50); 35 | [_titleButton setTitle:@"默认标题" forState:UIControlStateNormal]; 36 | [_titleButton setTitle:@"选中标题" forState:UIControlStateSelected]; 37 | _titleButton.backgroundColor = [UIColor yellowColor]; 38 | [_titleButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 39 | [_titleButton setTitleColor:[UIColor blackColor] forState:UIControlStateSelected]; 40 | _titleButton.titleLabel.font = [UIFont systemFontOfSize:14]; 41 | [_titleButton addTarget:self action:@selector(titleButtonAction:) forControlEvents:UIControlEventTouchUpInside]; 42 | [self addSubview:_titleButton]; 43 | } 44 | return _titleButton; 45 | } 46 | 47 | //当touch的pints在视图的子视图时,返回子视图,否则将事件透传到下面的视图 48 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 49 | UIView *hitTestView = [super hitTest:point withEvent:event]; 50 | if (hitTestView == self) { 51 | hitTestView = nil; 52 | } 53 | return hitTestView; 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /LMComposeView/LMTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // LMTableView.h 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMTableView : UITableView 12 | 13 | @property (nonatomic,assign) int dataSourceCount; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /LMComposeView/LMTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMTableView.m 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import "LMTableView.h" 10 | 11 | #define cellID @"cell" 12 | 13 | @interface LMTableView () 14 | 15 | @end 16 | 17 | @implementation LMTableView 18 | 19 | - (instancetype)init 20 | { 21 | self = [super init]; 22 | if (self) { 23 | 24 | self.separatorStyle = UITableViewCellSeparatorStyleNone; 25 | self.backgroundColor = [UIColor clearColor]; 26 | //注册 27 | [self registerClass:[UITableViewCell class] forCellReuseIdentifier:cellID]; 28 | 29 | self.delegate = self; 30 | self.dataSource = self; 31 | self.showsVerticalScrollIndicator = NO; 32 | self.showsHorizontalScrollIndicator = NO; 33 | self.dataSourceCount = 10; 34 | } 35 | return self; 36 | } 37 | 38 | -(void)setDataSourceCount:(int)dataSourceCount{ 39 | _dataSourceCount = dataSourceCount; 40 | 41 | [self reloadData]; 42 | } 43 | 44 | #pragma mark - dataSouce 45 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 46 | { 47 | return self.dataSourceCount; 48 | } 49 | 50 | -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 51 | UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath]; 52 | cell.textLabel.text =[NSString stringWithFormat:@"测试tableviewCell%p",self]; 53 | 54 | return cell; 55 | } 56 | 57 | //cell高度 58 | -(CGFloat )tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 59 | { 60 | return 40; 61 | } 62 | 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /LMComposeView/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /LMComposeView/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "DemoController.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (nonatomic,strong) UIButton * button; 15 | 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view, typically from a nib. 23 | 24 | [self.button class]; 25 | 26 | } 27 | 28 | -(void)pushAction{ 29 | DemoController * VC = [[DemoController alloc]init]; 30 | [self.navigationController pushViewController:VC animated:YES]; 31 | } 32 | 33 | -(UIButton *)button{ 34 | if (!_button) { 35 | _button = [[UIButton alloc]init]; 36 | _button.frame = CGRectMake(0, 0, 200, 50); 37 | _button.backgroundColor = [UIColor redColor]; 38 | [_button setTitle:@"推出测试界面" forState:UIControlStateNormal]; 39 | [_button addTarget:self action:@selector(pushAction) forControlEvents:UIControlEventTouchUpInside]; 40 | _button.center = self.view.center; 41 | [self.view addSubview:_button]; 42 | } 43 | return _button; 44 | } 45 | 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /LMComposeView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LMComposeView 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LMComposeViewTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LMComposeViewTests/LMComposeViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMComposeViewTests.m 3 | // LMComposeViewTests 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMComposeViewTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LMComposeViewTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LMComposeViewUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LMComposeViewUITests/LMComposeViewUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LMComposeViewUITests.m 3 | // LMComposeViewUITests 4 | // 5 | // Created by Leesim on 2018/8/7. 6 | // Copyright © 2018年 LiMing. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LMComposeViewUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LMComposeViewUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LMComposeView 2 | 3 | [原理详细讲解地址](https://www.jianshu.com/p/02e361fcb1ca) 4 | 5 | 你是否需要实现一个这种UITableView或UICollectionView(也可以是仅有其中一类)混合公用HeaderView的界面呢?大致效果如下方Demo动态图的效果:
6 | ![LMComposeViewDemoGif.gif](https://upload-images.jianshu.io/upload_images/1197929-a072197639798faa.gif?imageMogr2/auto-orient/strip) 7 | 8 | 9 | ### 使用方法 10 | 在使用LMComposeView的时候只要一行代码就能搞定: 11 | ``` 12 | #import "LMComposeView.h" 13 | @interface DemoController () 14 | @property(nonatomic,strong) LMComposeView * composeView; 15 | @end 16 | -(LMComposeView *)composeView{ 17 | if (!_composeView) { 18 | _composeView = [[LMComposeView alloc]init]; 19 | _composeView.delegate = self; 20 | [self.view addSubview:_composeView]; 21 | } 22 | return _composeView; 23 | } 24 | //LMComposeViewDelegate 返回当前选中的是第几个分类列表 25 | -(void)composeViewDidClickSegementButtonWithIndex:(NSInteger)index{ 26 | 27 | NSLog(@"---滚动到了%ld---",(long)index); 28 | } 29 | 30 | - (void)viewDidLoad { 31 | [super viewDidLoad]; 32 | //在初始化界面的时候 调用该方法 33 | [self.composeView confirmComposeViewWithScrollViewArray:scrollViewArray withSegmentButtonTitleArray:titleArray withHeaderView:self.headerView withComposeViewFrame:CGRectMake(0, 64,self.view.width, self.view.height-64)]; 34 | } 35 | ``` 36 | --------------------------------------------------------------------------------