PageRenderTime 27ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/TableViewCellWithAutoLayout/PureLayout/NSArray+PureLayout.m

https://gitlab.com/lisit1003/TableViewCellWithAutoLayoutiOS8
Objective C | 455 lines | 279 code | 30 blank | 146 comment | 41 complexity | ce790eac3aa66a326343ffd56209402f MD5 | raw file
  1. //
  2. // NSArray+PureLayout.m
  3. // v2.0.1
  4. // https://github.com/smileyborg/PureLayout
  5. //
  6. // Copyright (c) 2012 Richard Turton
  7. // Copyright (c) 2013-2014 Tyler Fox
  8. //
  9. // This code is distributed under the terms and conditions of the MIT license.
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining a copy
  12. // of this software and associated documentation files (the "Software"), to
  13. // deal in the Software without restriction, including without limitation the
  14. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  15. // sell copies of the Software, and to permit persons to whom the Software is
  16. // furnished to do so, subject to the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be included in
  19. // all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  26. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  27. // IN THE SOFTWARE.
  28. //
  29. #import "NSArray+PureLayout.h"
  30. #import "ALView+PureLayout.h"
  31. #import "NSLayoutConstraint+PureLayout.h"
  32. #import "PureLayout+Internal.h"
  33. #pragma mark - NSArray+PureLayout
  34. @implementation NSArray (PureLayout)
  35. #pragma mark Array of Constraints
  36. /**
  37. Activates the constraints in this array.
  38. */
  39. - (void)autoInstallConstraints
  40. {
  41. for (id object in self) {
  42. if ([object isKindOfClass:[NSLayoutConstraint class]]) {
  43. [((NSLayoutConstraint *)object) autoInstall];
  44. }
  45. }
  46. }
  47. /**
  48. Deactivates the constraints in this array.
  49. */
  50. - (void)autoRemoveConstraints
  51. {
  52. for (id object in self) {
  53. if ([object isKindOfClass:[NSLayoutConstraint class]]) {
  54. [((NSLayoutConstraint *)object) autoRemove];
  55. }
  56. }
  57. }
  58. #if __PureLayout_MinBaseSDK_iOS_8_0
  59. /**
  60. Sets the string as the identifier for the constraints in this array. Available in iOS 7.0 and OS X 10.9 and later.
  61. The identifer will be printed along with each constraint's description.
  62. This is helpful to document the constraints' purpose and aid in debugging.
  63. @param identifier A string used to identify the constraints in this array.
  64. @return This array.
  65. */
  66. - (instancetype)autoIdentifyConstraints:(NSString *)identifer
  67. {
  68. for (id object in self) {
  69. if ([object isKindOfClass:[NSLayoutConstraint class]]) {
  70. [((NSLayoutConstraint *)object) autoIdentify:identifer];
  71. }
  72. }
  73. return self;
  74. }
  75. #endif /* __PureLayout_MinBaseSDK_iOS_8_0 */
  76. #pragma mark Array of Views
  77. /**
  78. Aligns views in this array to one another along a given edge.
  79. Note: This array must contain at least 2 views, and all views must share a common superview.
  80. @param edge The edge to which the subviews will be aligned.
  81. @return An array of constraints added.
  82. */
  83. - (NSArray *)autoAlignViewsToEdge:(ALEdge)edge
  84. {
  85. NSAssert([self al_containsMinimumNumberOfViews:2], @"This array must contain at least 2 views.");
  86. NSMutableArray *constraints = [NSMutableArray new];
  87. ALView *previousView = nil;
  88. for (id object in self) {
  89. if ([object isKindOfClass:[ALView class]]) {
  90. ALView *view = (ALView *)object;
  91. view.translatesAutoresizingMaskIntoConstraints = NO;
  92. if (previousView) {
  93. [constraints addObject:[view autoPinEdge:edge toEdge:edge ofView:previousView]];
  94. }
  95. previousView = view;
  96. }
  97. }
  98. return constraints;
  99. }
  100. /**
  101. Aligns views in this array to one another along a given axis.
  102. Note: This array must contain at least 2 views, and all views must share a common superview.
  103. @param axis The axis to which to subviews will be aligned.
  104. @return An array of constraints added.
  105. */
  106. - (NSArray *)autoAlignViewsToAxis:(ALAxis)axis
  107. {
  108. NSAssert([self al_containsMinimumNumberOfViews:2], @"This array must contain at least 2 views.");
  109. NSMutableArray *constraints = [NSMutableArray new];
  110. ALView *previousView = nil;
  111. for (id object in self) {
  112. if ([object isKindOfClass:[ALView class]]) {
  113. ALView *view = (ALView *)object;
  114. view.translatesAutoresizingMaskIntoConstraints = NO;
  115. if (previousView) {
  116. [constraints addObject:[view autoAlignAxis:axis toSameAxisOfView:previousView]];
  117. }
  118. previousView = view;
  119. }
  120. }
  121. return constraints;
  122. }
  123. /**
  124. Matches a given dimension of all the views in this array.
  125. Note: This array must contain at least 2 views, and all views must share a common superview.
  126. @param dimension The dimension to match for all of the subviews.
  127. @return An array of constraints added.
  128. */
  129. - (NSArray *)autoMatchViewsDimension:(ALDimension)dimension
  130. {
  131. NSAssert([self al_containsMinimumNumberOfViews:2], @"This array must contain at least 2 views.");
  132. NSMutableArray *constraints = [NSMutableArray new];
  133. ALView *previousView = nil;
  134. for (id object in self) {
  135. if ([object isKindOfClass:[ALView class]]) {
  136. ALView *view = (ALView *)object;
  137. view.translatesAutoresizingMaskIntoConstraints = NO;
  138. if (previousView) {
  139. [constraints addObject:[view autoMatchDimension:dimension toDimension:dimension ofView:previousView]];
  140. }
  141. previousView = view;
  142. }
  143. }
  144. return constraints;
  145. }
  146. /**
  147. Sets the given dimension of all the views in this array to a given size.
  148. Note: This array must contain at least 1 view.
  149. @param dimension The dimension of each of the subviews to set.
  150. @param size The size to set the given dimension of each subview to.
  151. @return An array of constraints added.
  152. */
  153. - (NSArray *)autoSetViewsDimension:(ALDimension)dimension toSize:(CGFloat)size
  154. {
  155. NSAssert([self al_containsMinimumNumberOfViews:1], @"This array must contain at least 1 view.");
  156. NSMutableArray *constraints = [NSMutableArray new];
  157. for (id object in self) {
  158. if ([object isKindOfClass:[ALView class]]) {
  159. ALView *view = (ALView *)object;
  160. view.translatesAutoresizingMaskIntoConstraints = NO;
  161. [constraints addObject:[view autoSetDimension:dimension toSize:size]];
  162. }
  163. }
  164. return constraints;
  165. }
  166. /**
  167. Distributes the views in this array equally along the selected axis in their superview.
  168. Views will be the same size (variable) in the dimension along the axis and will have spacing (fixed) between them,
  169. including from the first and last views to their superview.
  170. @param axis The axis along which to distribute the views.
  171. @param alignment The attribute to use to align all the views to one another.
  172. @param spacing The fixed amount of spacing between each subview, before the first subview and after the last subview.
  173. @return An array of constraints added.
  174. */
  175. - (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
  176. alignedTo:(ALAttribute)alignment
  177. withFixedSpacing:(CGFloat)spacing
  178. {
  179. return [self autoDistributeViewsAlongAxis:axis
  180. alignedTo:alignment
  181. withFixedSpacing:spacing
  182. insetSpacing:YES];
  183. }
  184. /**
  185. Distributes the views in this array equally along the selected axis in their superview.
  186. Views will be the same size (variable) in the dimension along the axis and will have spacing (fixed) between them.
  187. The first and last views can optionally be inset from their superview by the same amount of spacing as between views.
  188. @param axis The axis along which to distribute the views.
  189. @param alignment The attribute to use to align all the views to one another.
  190. @param spacing The fixed amount of spacing between each subview.
  191. @param shouldSpaceInsets Whether the first and last views should be equally inset from their superview.
  192. @return An array of constraints added.
  193. */
  194. - (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
  195. alignedTo:(ALAttribute)alignment
  196. withFixedSpacing:(CGFloat)spacing
  197. insetSpacing:(BOOL)shouldSpaceInsets
  198. {
  199. return [self autoDistributeViewsAlongAxis:axis
  200. alignedTo:alignment
  201. withFixedSpacing:spacing
  202. insetSpacing:shouldSpaceInsets
  203. matchedSizes:YES];
  204. }
  205. /**
  206. Distributes the views in this array equally along the selected axis in their superview.
  207. Views will have fixed spacing between them, and can optionally be constrained to the same size in the dimension along the axis.
  208. The first and last views can optionally be inset from their superview by the same amount of spacing as between views.
  209. @param axis The axis along which to distribute the views.
  210. @param alignment The attribute to use to align all the views to one another.
  211. @param spacing The fixed amount of spacing between each view.
  212. @param shouldSpaceInsets Whether the first and last views should be equally inset from their superview.
  213. @param shouldMatchSizes Whether all views will be constrained to be the same size in the dimension along the axis.
  214. NOTE: All views must specify an intrinsic content size if passing NO, otherwise the layout will be ambiguous!
  215. @return An array of constraints added.
  216. */
  217. - (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
  218. alignedTo:(ALAttribute)alignment
  219. withFixedSpacing:(CGFloat)spacing
  220. insetSpacing:(BOOL)shouldSpaceInsets
  221. matchedSizes:(BOOL)shouldMatchSizes
  222. {
  223. NSAssert([self al_containsMinimumNumberOfViews:2], @"This array must contain at least 2 views to distribute.");
  224. ALDimension matchedDimension;
  225. ALEdge firstEdge, lastEdge;
  226. switch (axis) {
  227. case ALAxisHorizontal:
  228. case ALAxisBaseline: // same value as ALAxisLastBaseline
  229. #if __PureLayout_MinBaseSDK_iOS_8_0
  230. case ALAxisFirstBaseline:
  231. #endif /* __PureLayout_MinBaseSDK_iOS_8_0 */
  232. matchedDimension = ALDimensionWidth;
  233. firstEdge = ALEdgeLeading;
  234. lastEdge = ALEdgeTrailing;
  235. break;
  236. case ALAxisVertical:
  237. matchedDimension = ALDimensionHeight;
  238. firstEdge = ALEdgeTop;
  239. lastEdge = ALEdgeBottom;
  240. break;
  241. default:
  242. NSAssert(nil, @"Not a valid ALAxis.");
  243. return nil;
  244. }
  245. CGFloat leadingSpacing = shouldSpaceInsets ? spacing : 0.0;
  246. CGFloat trailingSpacing = shouldSpaceInsets ? spacing : 0.0;
  247. NSMutableArray *constraints = [NSMutableArray new];
  248. ALView *previousView = nil;
  249. for (id object in self) {
  250. if ([object isKindOfClass:[ALView class]]) {
  251. ALView *view = (ALView *)object;
  252. view.translatesAutoresizingMaskIntoConstraints = NO;
  253. if (previousView) {
  254. // Second, Third, ... View
  255. [constraints addObject:[view autoPinEdge:firstEdge toEdge:lastEdge ofView:previousView withOffset:spacing]];
  256. if (shouldMatchSizes) {
  257. [constraints addObject:[view autoMatchDimension:matchedDimension toDimension:matchedDimension ofView:previousView]];
  258. }
  259. [constraints addObject:[view al_alignAttribute:alignment toView:previousView forAxis:axis]];
  260. }
  261. else {
  262. // First view
  263. [constraints addObject:[view autoPinEdgeToSuperviewEdge:firstEdge withInset:leadingSpacing]];
  264. }
  265. previousView = view;
  266. }
  267. }
  268. if (previousView) {
  269. // Last View
  270. [constraints addObject:[previousView autoPinEdgeToSuperviewEdge:lastEdge withInset:trailingSpacing]];
  271. }
  272. return constraints;
  273. }
  274. /**
  275. Distributes the views in this array equally along the selected axis in their superview.
  276. Views will be the same size (fixed) in the dimension along the axis and will have spacing (variable) between them,
  277. including from the first and last views to their superview.
  278. @param axis The axis along which to distribute the views.
  279. @param alignment The attribute to use to align all the views to one another.
  280. @param size The fixed size of each view in the dimension along the given axis.
  281. @return An array of constraints added.
  282. */
  283. - (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
  284. alignedTo:(ALAttribute)alignment
  285. withFixedSize:(CGFloat)size
  286. {
  287. return [self autoDistributeViewsAlongAxis:axis
  288. alignedTo:alignment
  289. withFixedSize:size
  290. insetSpacing:YES];
  291. }
  292. /**
  293. Distributes the views in this array equally along the selected axis in their superview.
  294. Views will be the same size (fixed) in the dimension along the axis and will have spacing (variable) between them.
  295. The first and last views can optionally be inset from their superview by the same amount of spacing as between views.
  296. @param axis The axis along which to distribute the views.
  297. @param alignment The attribute to use to align all the views to one another.
  298. @param size The fixed size of each view in the dimension along the given axis.
  299. @param shouldSpaceInsets Whether the first and last views should be equally inset from their superview.
  300. @return An array of constraints added.
  301. */
  302. - (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
  303. alignedTo:(ALAttribute)alignment
  304. withFixedSize:(CGFloat)size
  305. insetSpacing:(BOOL)shouldSpaceInsets
  306. {
  307. NSAssert([self al_containsMinimumNumberOfViews:2], @"This array must contain at least 2 views to distribute.");
  308. ALDimension fixedDimension;
  309. NSLayoutAttribute attribute;
  310. switch (axis) {
  311. case ALAxisHorizontal:
  312. case ALAxisBaseline: // same value as ALAxisLastBaseline
  313. #if __PureLayout_MinBaseSDK_iOS_8_0
  314. case ALAxisFirstBaseline:
  315. #endif /* __PureLayout_MinBaseSDK_iOS_8_0 */
  316. fixedDimension = ALDimensionWidth;
  317. attribute = NSLayoutAttributeCenterX;
  318. break;
  319. case ALAxisVertical:
  320. fixedDimension = ALDimensionHeight;
  321. attribute = NSLayoutAttributeCenterY;
  322. break;
  323. default:
  324. NSAssert(nil, @"Not a valid ALAxis.");
  325. return nil;
  326. }
  327. BOOL isRightToLeftLanguage = [NSLocale characterDirectionForLanguage:[[NSBundle mainBundle] preferredLocalizations][0]] == NSLocaleLanguageDirectionRightToLeft;
  328. BOOL shouldFlipOrder = isRightToLeftLanguage && (axis != ALAxisVertical); // imitate the effect of leading/trailing when distributing horizontally
  329. NSMutableArray *constraints = [NSMutableArray new];
  330. NSArray *views = [self al_copyViewsOnly];
  331. NSUInteger numberOfViews = [views count];
  332. ALView *commonSuperview = [views al_commonSuperviewOfViews];
  333. ALView *previousView = nil;
  334. for (NSUInteger i = 0; i < numberOfViews; i++) {
  335. ALView *view = shouldFlipOrder ? views[numberOfViews - i - 1] : views[i];
  336. view.translatesAutoresizingMaskIntoConstraints = NO;
  337. [constraints addObject:[view autoSetDimension:fixedDimension toSize:size]];
  338. CGFloat multiplier, constant;
  339. if (shouldSpaceInsets) {
  340. multiplier = (i * 2.0 + 2.0) / (numberOfViews + 1.0);
  341. constant = (multiplier - 1.0) * size / 2.0;
  342. } else {
  343. multiplier = (i * 2.0) / (numberOfViews - 1.0);
  344. constant = (-multiplier + 1.0) * size / 2.0;
  345. }
  346. NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view attribute:attribute relatedBy:NSLayoutRelationEqual toItem:commonSuperview attribute:attribute multiplier:multiplier constant:constant];
  347. [constraint autoInstall];
  348. [constraints addObject:constraint];
  349. if (previousView) {
  350. [constraints addObject:[view al_alignAttribute:alignment toView:previousView forAxis:axis]];
  351. }
  352. previousView = view;
  353. }
  354. return constraints;
  355. }
  356. #pragma mark Internal Helper Methods
  357. /**
  358. Returns the common superview for the views in this array.
  359. Raises an exception if the views in this array do not share a common superview.
  360. @return The common superview for the views in this array.
  361. */
  362. - (ALView *)al_commonSuperviewOfViews
  363. {
  364. ALView *commonSuperview = nil;
  365. ALView *previousView = nil;
  366. for (id object in self) {
  367. if ([object isKindOfClass:[ALView class]]) {
  368. ALView *view = (ALView *)object;
  369. if (previousView) {
  370. commonSuperview = [view al_commonSuperviewWithView:commonSuperview];
  371. } else {
  372. commonSuperview = view;
  373. }
  374. previousView = view;
  375. }
  376. }
  377. NSAssert(commonSuperview, @"Can't constrain views that do not share a common superview. Make sure that all the views in this array have been added into the same view hierarchy.");
  378. return commonSuperview;
  379. }
  380. /**
  381. Determines whether this array contains a minimum number of views.
  382. @param minimumNumberOfViews The minimum number of views to check for.
  383. @return YES if this array contains at least the minimum number of views, NO otherwise.
  384. */
  385. - (BOOL)al_containsMinimumNumberOfViews:(NSUInteger)minimumNumberOfViews
  386. {
  387. NSUInteger numberOfViews = 0;
  388. for (id object in self) {
  389. if ([object isKindOfClass:[ALView class]]) {
  390. numberOfViews++;
  391. if (numberOfViews >= minimumNumberOfViews) {
  392. return YES;
  393. }
  394. }
  395. }
  396. return numberOfViews >= minimumNumberOfViews;
  397. }
  398. /**
  399. Creates a copy of this array containing only the view objects in it.
  400. @return A new array containing only the views that are in this array.
  401. */
  402. - (NSArray *)al_copyViewsOnly
  403. {
  404. NSMutableArray *viewsOnlyArray = [NSMutableArray arrayWithCapacity:[self count]];
  405. for (id object in self) {
  406. if ([object isKindOfClass:[ALView class]]) {
  407. [viewsOnlyArray addObject:object];
  408. }
  409. }
  410. return viewsOnlyArray;
  411. }
  412. @end