POPAnimatableProperty.mm 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307
  1. /**
  2. Copyright (c) 2014-present, Facebook, Inc.
  3. All rights reserved.
  4. This source code is licensed under the BSD-style license found in the
  5. LICENSE file in the root directory of this source tree. An additional grant
  6. of patent rights can be found in the PATENTS file in the same directory.
  7. */
  8. #import "POPAnimatableProperty.h"
  9. #import <QuartzCore/QuartzCore.h>
  10. #import "POPAnimationRuntime.h"
  11. #import "POPCGUtils.h"
  12. #import "POPDefines.h"
  13. #import "POPLayerExtras.h"
  14. // common threshold definitions
  15. static CGFloat const kPOPThresholdColor = 0.01;
  16. static CGFloat const kPOPThresholdPoint = 1.0;
  17. static CGFloat const kPOPThresholdOpacity = 0.01;
  18. static CGFloat const kPOPThresholdScale = 0.005;
  19. static CGFloat const kPOPThresholdRotation = 0.01;
  20. static CGFloat const kPOPThresholdRadius = 0.01;
  21. #pragma mark - Static
  22. // CALayer
  23. NSString * const kPOPLayerBackgroundColor = @"backgroundColor";
  24. NSString * const kPOPLayerBounds = @"bounds";
  25. NSString * const kPOPLayerCornerRadius = @"cornerRadius";
  26. NSString * const kPOPLayerBorderWidth = @"borderWidth";
  27. NSString * const kPOPLayerBorderColor = @"borderColor";
  28. NSString * const kPOPLayerOpacity = @"opacity";
  29. NSString * const kPOPLayerPosition = @"position";
  30. NSString * const kPOPLayerPositionX = @"positionX";
  31. NSString * const kPOPLayerPositionY = @"positionY";
  32. NSString * const kPOPLayerRotation = @"rotation";
  33. NSString * const kPOPLayerRotationX = @"rotationX";
  34. NSString * const kPOPLayerRotationY = @"rotationY";
  35. NSString * const kPOPLayerScaleX = @"scaleX";
  36. NSString * const kPOPLayerScaleXY = @"scaleXY";
  37. NSString * const kPOPLayerScaleY = @"scaleY";
  38. NSString * const kPOPLayerSize = @"size";
  39. NSString * const kPOPLayerSubscaleXY = @"subscaleXY";
  40. NSString * const kPOPLayerSubtranslationX = @"subtranslationX";
  41. NSString * const kPOPLayerSubtranslationXY = @"subtranslationXY";
  42. NSString * const kPOPLayerSubtranslationY = @"subtranslationY";
  43. NSString * const kPOPLayerSubtranslationZ = @"subtranslationZ";
  44. NSString * const kPOPLayerTranslationX = @"translationX";
  45. NSString * const kPOPLayerTranslationXY = @"translationXY";
  46. NSString * const kPOPLayerTranslationY = @"translationY";
  47. NSString * const kPOPLayerTranslationZ = @"translationZ";
  48. NSString * const kPOPLayerZPosition = @"zPosition";
  49. NSString * const kPOPLayerShadowColor = @"shadowColor";
  50. NSString * const kPOPLayerShadowOffset = @"shadowOffset";
  51. NSString * const kPOPLayerShadowOpacity = @"shadowOpacity";
  52. NSString * const kPOPLayerShadowRadius = @"shadowRadius";
  53. // CAShapeLayer
  54. NSString * const kPOPShapeLayerStrokeStart = @"shapeLayer.strokeStart";
  55. NSString * const kPOPShapeLayerStrokeEnd = @"shapeLayer.strokeEnd";
  56. NSString * const kPOPShapeLayerStrokeColor = @"shapeLayer.strokeColor";
  57. NSString * const kPOPShapeLayerFillColor = @"shapeLayer.fillColor";
  58. NSString * const kPOPShapeLayerLineWidth = @"shapeLayer.lineWidth";
  59. NSString * const kPOPShapeLayerLineDashPhase = @"shapeLayer.lineDashPhase";
  60. // NSLayoutConstraint
  61. NSString * const kPOPLayoutConstraintConstant = @"layoutConstraint.constant";
  62. #if TARGET_OS_IPHONE
  63. // UIView
  64. NSString * const kPOPViewAlpha = @"view.alpha";
  65. NSString * const kPOPViewBackgroundColor = @"view.backgroundColor";
  66. NSString * const kPOPViewBounds = kPOPLayerBounds;
  67. NSString * const kPOPViewCenter = @"view.center";
  68. NSString * const kPOPViewFrame = @"view.frame";
  69. NSString * const kPOPViewScaleX = @"view.scaleX";
  70. NSString * const kPOPViewScaleXY = @"view.scaleXY";
  71. NSString * const kPOPViewScaleY = @"view.scaleY";
  72. NSString * const kPOPViewSize = kPOPLayerSize;
  73. NSString * const kPOPViewTintColor = @"view.tintColor";
  74. // UIScrollView
  75. NSString * const kPOPScrollViewContentOffset = @"scrollView.contentOffset";
  76. NSString * const kPOPScrollViewContentSize = @"scrollView.contentSize";
  77. NSString * const kPOPScrollViewZoomScale = @"scrollView.zoomScale";
  78. NSString * const kPOPScrollViewContentInset = @"scrollView.contentInset";
  79. NSString * const kPOPScrollViewScrollIndicatorInsets = @"scrollView.scrollIndicatorInsets";
  80. // UITableView
  81. NSString * const kPOPTableViewContentOffset = kPOPScrollViewContentOffset;
  82. NSString * const kPOPTableViewContentSize = kPOPScrollViewContentSize;
  83. // UICollectionView
  84. NSString * const kPOPCollectionViewContentOffset = kPOPScrollViewContentOffset;
  85. NSString * const kPOPCollectionViewContentSize = kPOPScrollViewContentSize;
  86. // UINavigationBar
  87. NSString * const kPOPNavigationBarBarTintColor = @"navigationBar.barTintColor";
  88. // UIToolbar
  89. NSString * const kPOPToolbarBarTintColor = kPOPNavigationBarBarTintColor;
  90. // UITabBar
  91. NSString * const kPOPTabBarBarTintColor = kPOPNavigationBarBarTintColor;
  92. // UILabel
  93. NSString * const kPOPLabelTextColor = @"label.textColor";
  94. #else
  95. // NSView
  96. NSString * const kPOPViewFrame = @"view.frame";
  97. NSString * const kPOPViewBounds = @"view.bounds";
  98. NSString * const kPOPViewAlphaValue = @"view.alphaValue";
  99. NSString * const kPOPViewFrameRotation = @"view.frameRotation";
  100. NSString * const kPOPViewFrameCenterRotation = @"view.frameCenterRotation";
  101. NSString * const kPOPViewBoundsRotation = @"view.boundsRotation";
  102. // NSWindow
  103. NSString * const kPOPWindowFrame = @"window.frame";
  104. NSString * const kPOPWindowAlphaValue = @"window.alphaValue";
  105. NSString * const kPOPWindowBackgroundColor = @"window.backgroundColor";
  106. #endif
  107. #if SCENEKIT_SDK_AVAILABLE
  108. // SceneKit
  109. NSString * const kPOPSCNNodePosition = @"scnode.position";
  110. NSString * const kPOPSCNNodePositionX = @"scnnode.position.x";
  111. NSString * const kPOPSCNNodePositionY = @"scnnode.position.y";
  112. NSString * const kPOPSCNNodePositionZ = @"scnnode.position.z";
  113. NSString * const kPOPSCNNodeTranslation = @"scnnode.translation";
  114. NSString * const kPOPSCNNodeTranslationX = @"scnnode.translation.x";
  115. NSString * const kPOPSCNNodeTranslationY = @"scnnode.translation.y";
  116. NSString * const kPOPSCNNodeTranslationZ = @"scnnode.translation.z";
  117. NSString * const kPOPSCNNodeRotation = @"scnnode.rotation";
  118. NSString * const kPOPSCNNodeRotationX = @"scnnode.rotation.x";
  119. NSString * const kPOPSCNNodeRotationY = @"scnnode.rotation.y";
  120. NSString * const kPOPSCNNodeRotationZ = @"scnnode.rotation.z";
  121. NSString * const kPOPSCNNodeRotationW = @"scnnode.rotation.w";
  122. NSString * const kPOPSCNNodeEulerAngles = @"scnnode.eulerAngles";
  123. NSString * const kPOPSCNNodeEulerAnglesX = @"scnnode.eulerAngles.x";
  124. NSString * const kPOPSCNNodeEulerAnglesY = @"scnnode.eulerAngles.y";
  125. NSString * const kPOPSCNNodeEulerAnglesZ = @"scnnode.eulerAngles.z";
  126. NSString * const kPOPSCNNodeOrientation = @"scnnode.orientation";
  127. NSString * const kPOPSCNNodeOrientationX = @"scnnode.orientation.x";
  128. NSString * const kPOPSCNNodeOrientationY = @"scnnode.orientation.y";
  129. NSString * const kPOPSCNNodeOrientationZ = @"scnnode.orientation.z";
  130. NSString * const kPOPSCNNodeOrientationW = @"scnnode.orientation.w";
  131. NSString * const kPOPSCNNodeScale = @"scnnode.scale";
  132. NSString * const kPOPSCNNodeScaleX = @"scnnode.scale.x";
  133. NSString * const kPOPSCNNodeScaleY = @"scnnode.scale.y";
  134. NSString * const kPOPSCNNodeScaleZ = @"scnnode.scale.z";
  135. NSString * const kPOPSCNNodeScaleXY = @"scnnode.scale.xy";
  136. #endif
  137. /**
  138. State structure internal to static animatable property.
  139. */
  140. typedef struct
  141. {
  142. NSString *name;
  143. pop_animatable_read_block readBlock;
  144. pop_animatable_write_block writeBlock;
  145. CGFloat threshold;
  146. } _POPStaticAnimatablePropertyState;
  147. typedef _POPStaticAnimatablePropertyState POPStaticAnimatablePropertyState;
  148. static POPStaticAnimatablePropertyState _staticStates[] =
  149. {
  150. /* CALayer */
  151. {kPOPLayerBackgroundColor,
  152. ^(CALayer *obj, CGFloat values[]) {
  153. POPCGColorGetRGBAComponents(obj.backgroundColor, values);
  154. },
  155. ^(CALayer *obj, const CGFloat values[]) {
  156. CGColorRef color = POPCGColorRGBACreate(values);
  157. [obj setBackgroundColor:color];
  158. CGColorRelease(color);
  159. },
  160. kPOPThresholdColor
  161. },
  162. {kPOPLayerBounds,
  163. ^(CALayer *obj, CGFloat values[]) {
  164. values_from_rect(values, [obj bounds]);
  165. },
  166. ^(CALayer *obj, const CGFloat values[]) {
  167. [obj setBounds:values_to_rect(values)];
  168. },
  169. kPOPThresholdPoint
  170. },
  171. {kPOPLayerCornerRadius,
  172. ^(CALayer *obj, CGFloat values[]) {
  173. values[0] = [obj cornerRadius];
  174. },
  175. ^(CALayer *obj, const CGFloat values[]) {
  176. [obj setCornerRadius:values[0]];
  177. },
  178. kPOPThresholdRadius
  179. },
  180. {kPOPLayerBorderWidth,
  181. ^(CALayer *obj, CGFloat values[]) {
  182. values[0] = [obj borderWidth];
  183. },
  184. ^(CALayer *obj, const CGFloat values[]) {
  185. [obj setBorderWidth:values[0]];
  186. },
  187. 0.01
  188. },
  189. {kPOPLayerBorderColor,
  190. ^(CALayer *obj, CGFloat values[]) {
  191. POPCGColorGetRGBAComponents(obj.borderColor, values);
  192. },
  193. ^(CALayer *obj, const CGFloat values[]) {
  194. CGColorRef color = POPCGColorRGBACreate(values);
  195. [obj setBorderColor:color];
  196. CGColorRelease(color);
  197. },
  198. kPOPThresholdColor
  199. },
  200. {kPOPLayerPosition,
  201. ^(CALayer *obj, CGFloat values[]) {
  202. values_from_point(values, [(CALayer *)obj position]);
  203. },
  204. ^(CALayer *obj, const CGFloat values[]) {
  205. [obj setPosition:values_to_point(values)];
  206. },
  207. kPOPThresholdPoint
  208. },
  209. {kPOPLayerPositionX,
  210. ^(CALayer *obj, CGFloat values[]) {
  211. values[0] = [(CALayer *)obj position].x;
  212. },
  213. ^(CALayer *obj, const CGFloat values[]) {
  214. CGPoint p = [(CALayer *)obj position];
  215. p.x = values[0];
  216. [obj setPosition:p];
  217. },
  218. kPOPThresholdPoint
  219. },
  220. {kPOPLayerPositionY,
  221. ^(CALayer *obj, CGFloat values[]) {
  222. values[0] = [(CALayer *)obj position].y;
  223. },
  224. ^(CALayer *obj, const CGFloat values[]) {
  225. CGPoint p = [(CALayer *)obj position];
  226. p.y = values[0];
  227. [obj setPosition:p];
  228. },
  229. kPOPThresholdPoint
  230. },
  231. {kPOPLayerOpacity,
  232. ^(CALayer *obj, CGFloat values[]) {
  233. values[0] = [obj opacity];
  234. },
  235. ^(CALayer *obj, const CGFloat values[]) {
  236. [obj setOpacity:((float)values[0])];
  237. },
  238. kPOPThresholdOpacity
  239. },
  240. {kPOPLayerScaleX,
  241. ^(CALayer *obj, CGFloat values[]) {
  242. values[0] = POPLayerGetScaleX(obj);
  243. },
  244. ^(CALayer *obj, const CGFloat values[]) {
  245. POPLayerSetScaleX(obj, values[0]);
  246. },
  247. kPOPThresholdScale
  248. },
  249. {kPOPLayerScaleY,
  250. ^(CALayer *obj, CGFloat values[]) {
  251. values[0] = POPLayerGetScaleY(obj);
  252. },
  253. ^(CALayer *obj, const CGFloat values[]) {
  254. POPLayerSetScaleY(obj, values[0]);
  255. },
  256. kPOPThresholdScale
  257. },
  258. {kPOPLayerScaleXY,
  259. ^(CALayer *obj, CGFloat values[]) {
  260. values_from_point(values, POPLayerGetScaleXY(obj));
  261. },
  262. ^(CALayer *obj, const CGFloat values[]) {
  263. POPLayerSetScaleXY(obj, values_to_point(values));
  264. },
  265. kPOPThresholdScale
  266. },
  267. {kPOPLayerSubscaleXY,
  268. ^(CALayer *obj, CGFloat values[]) {
  269. values_from_point(values, POPLayerGetSubScaleXY(obj));
  270. },
  271. ^(CALayer *obj, const CGFloat values[]) {
  272. POPLayerSetSubScaleXY(obj, values_to_point(values));
  273. },
  274. kPOPThresholdScale
  275. },
  276. {kPOPLayerTranslationX,
  277. ^(CALayer *obj, CGFloat values[]) {
  278. values[0] = POPLayerGetTranslationX(obj);
  279. },
  280. ^(CALayer *obj, const CGFloat values[]) {
  281. POPLayerSetTranslationX(obj, values[0]);
  282. },
  283. kPOPThresholdPoint
  284. },
  285. {kPOPLayerTranslationY,
  286. ^(CALayer *obj, CGFloat values[]) {
  287. values[0] = POPLayerGetTranslationY(obj);
  288. },
  289. ^(CALayer *obj, const CGFloat values[]) {
  290. POPLayerSetTranslationY(obj, values[0]);
  291. },
  292. kPOPThresholdPoint
  293. },
  294. {kPOPLayerTranslationZ,
  295. ^(CALayer *obj, CGFloat values[]) {
  296. values[0] = POPLayerGetTranslationZ(obj);
  297. },
  298. ^(CALayer *obj, const CGFloat values[]) {
  299. POPLayerSetTranslationZ(obj, values[0]);
  300. },
  301. kPOPThresholdPoint
  302. },
  303. {kPOPLayerTranslationXY,
  304. ^(CALayer *obj, CGFloat values[]) {
  305. values_from_point(values, POPLayerGetTranslationXY(obj));
  306. },
  307. ^(CALayer *obj, const CGFloat values[]) {
  308. POPLayerSetTranslationXY(obj, values_to_point(values));
  309. },
  310. kPOPThresholdPoint
  311. },
  312. {kPOPLayerSubtranslationX,
  313. ^(CALayer *obj, CGFloat values[]) {
  314. values[0] = POPLayerGetSubTranslationX(obj);
  315. },
  316. ^(CALayer *obj, const CGFloat values[]) {
  317. POPLayerSetSubTranslationX(obj, values[0]);
  318. },
  319. kPOPThresholdPoint
  320. },
  321. {kPOPLayerSubtranslationY,
  322. ^(CALayer *obj, CGFloat values[]) {
  323. values[0] = POPLayerGetSubTranslationY(obj);
  324. },
  325. ^(CALayer *obj, const CGFloat values[]) {
  326. POPLayerSetSubTranslationY(obj, values[0]);
  327. },
  328. kPOPThresholdPoint
  329. },
  330. {kPOPLayerSubtranslationZ,
  331. ^(CALayer *obj, CGFloat values[]) {
  332. values[0] = POPLayerGetSubTranslationZ(obj);
  333. },
  334. ^(CALayer *obj, const CGFloat values[]) {
  335. POPLayerSetSubTranslationZ(obj, values[0]);
  336. },
  337. kPOPThresholdPoint
  338. },
  339. {kPOPLayerSubtranslationXY,
  340. ^(CALayer *obj, CGFloat values[]) {
  341. values_from_point(values, POPLayerGetSubTranslationXY(obj));
  342. },
  343. ^(CALayer *obj, const CGFloat values[]) {
  344. POPLayerSetSubTranslationXY(obj, values_to_point(values));
  345. },
  346. kPOPThresholdPoint
  347. },
  348. {kPOPLayerZPosition,
  349. ^(CALayer *obj, CGFloat values[]) {
  350. values[0] = [obj zPosition];
  351. },
  352. ^(CALayer *obj, const CGFloat values[]) {
  353. [obj setZPosition:values[0]];
  354. },
  355. kPOPThresholdPoint
  356. },
  357. {kPOPLayerSize,
  358. ^(CALayer *obj, CGFloat values[]) {
  359. values_from_size(values, [obj bounds].size);
  360. },
  361. ^(CALayer *obj, const CGFloat values[]) {
  362. CGSize size = values_to_size(values);
  363. if (size.width < 0. || size.height < 0.)
  364. return;
  365. CGRect b = [obj bounds];
  366. b.size = size;
  367. [obj setBounds:b];
  368. },
  369. kPOPThresholdPoint
  370. },
  371. {kPOPLayerRotation,
  372. ^(CALayer *obj, CGFloat values[]) {
  373. values[0] = POPLayerGetRotation(obj);
  374. },
  375. ^(CALayer *obj, const CGFloat values[]) {
  376. POPLayerSetRotation(obj, values[0]);
  377. },
  378. kPOPThresholdRotation
  379. },
  380. {kPOPLayerRotationY,
  381. ^(CALayer *obj, CGFloat values[]) {
  382. values[0] = POPLayerGetRotationY(obj);
  383. },
  384. ^(id obj, const CGFloat values[]) {
  385. POPLayerSetRotationY(obj, values[0]);
  386. },
  387. kPOPThresholdRotation
  388. },
  389. {kPOPLayerRotationX,
  390. ^(CALayer *obj, CGFloat values[]) {
  391. values[0] = POPLayerGetRotationX(obj);
  392. },
  393. ^(CALayer *obj, const CGFloat values[]) {
  394. POPLayerSetRotationX(obj, values[0]);
  395. },
  396. kPOPThresholdRotation
  397. },
  398. {kPOPLayerShadowColor,
  399. ^(CALayer *obj, CGFloat values[]) {
  400. POPCGColorGetRGBAComponents(obj.shadowColor, values);
  401. },
  402. ^(CALayer *obj, const CGFloat values[]) {
  403. CGColorRef color = POPCGColorRGBACreate(values);
  404. [obj setShadowColor:color];
  405. CGColorRelease(color);
  406. },
  407. 0.01
  408. },
  409. {kPOPLayerShadowOffset,
  410. ^(CALayer *obj, CGFloat values[]) {
  411. values_from_size(values, [obj shadowOffset]);
  412. },
  413. ^(CALayer *obj, const CGFloat values[]) {
  414. CGSize size = values_to_size(values);
  415. [obj setShadowOffset:size];
  416. },
  417. 0.01
  418. },
  419. {kPOPLayerShadowOpacity,
  420. ^(CALayer *obj, CGFloat values[]) {
  421. values[0] = [obj shadowOpacity];
  422. },
  423. ^(CALayer *obj, const CGFloat values[]) {
  424. [obj setShadowOpacity:values[0]];
  425. },
  426. kPOPThresholdOpacity
  427. },
  428. {kPOPLayerShadowRadius,
  429. ^(CALayer *obj, CGFloat values[]) {
  430. values[0] = [obj shadowRadius];
  431. },
  432. ^(CALayer *obj, const CGFloat values[]) {
  433. [obj setShadowRadius:values[0]];
  434. },
  435. kPOPThresholdRadius
  436. },
  437. /* CAShapeLayer */
  438. {kPOPShapeLayerStrokeStart,
  439. ^(CAShapeLayer *obj, CGFloat values[]) {
  440. values[0] = obj.strokeStart;
  441. },
  442. ^(CAShapeLayer *obj, const CGFloat values[]) {
  443. obj.strokeStart = values[0];
  444. },
  445. 0.01
  446. },
  447. {kPOPShapeLayerStrokeEnd,
  448. ^(CAShapeLayer *obj, CGFloat values[]) {
  449. values[0] = obj.strokeEnd;
  450. },
  451. ^(CAShapeLayer *obj, const CGFloat values[]) {
  452. obj.strokeEnd = values[0];
  453. },
  454. 0.01
  455. },
  456. {kPOPShapeLayerStrokeColor,
  457. ^(CAShapeLayer *obj, CGFloat values[]) {
  458. POPCGColorGetRGBAComponents(obj.strokeColor, values);
  459. },
  460. ^(CAShapeLayer *obj, const CGFloat values[]) {
  461. CGColorRef color = POPCGColorRGBACreate(values);
  462. [obj setStrokeColor:color];
  463. CGColorRelease(color);
  464. },
  465. kPOPThresholdColor
  466. },
  467. {kPOPShapeLayerFillColor,
  468. ^(CAShapeLayer *obj, CGFloat values[]) {
  469. POPCGColorGetRGBAComponents(obj.fillColor, values);
  470. },
  471. ^(CAShapeLayer *obj, const CGFloat values[]) {
  472. CGColorRef color = POPCGColorRGBACreate(values);
  473. [obj setFillColor:color];
  474. CGColorRelease(color);
  475. },
  476. kPOPThresholdColor
  477. },
  478. {kPOPShapeLayerLineWidth,
  479. ^(CAShapeLayer *obj, CGFloat values[]) {
  480. values[0] = obj.lineWidth;
  481. },
  482. ^(CAShapeLayer *obj, const CGFloat values[]) {
  483. obj.lineWidth = values[0];
  484. },
  485. 0.01
  486. },
  487. {kPOPShapeLayerLineDashPhase,
  488. ^(CAShapeLayer *obj, CGFloat values[]) {
  489. values[0] = obj.lineDashPhase;
  490. },
  491. ^(CAShapeLayer *obj, const CGFloat values[]) {
  492. obj.lineDashPhase = values[0];
  493. },
  494. 0.01
  495. },
  496. {kPOPLayoutConstraintConstant,
  497. ^(NSLayoutConstraint *obj, CGFloat values[]) {
  498. values[0] = obj.constant;
  499. },
  500. ^(NSLayoutConstraint *obj, const CGFloat values[]) {
  501. obj.constant = values[0];
  502. },
  503. 0.01
  504. },
  505. #if TARGET_OS_IPHONE
  506. /* UIView */
  507. {kPOPViewAlpha,
  508. ^(UIView *obj, CGFloat values[]) {
  509. values[0] = obj.alpha;
  510. },
  511. ^(UIView *obj, const CGFloat values[]) {
  512. obj.alpha = values[0];
  513. },
  514. kPOPThresholdOpacity
  515. },
  516. {kPOPViewBackgroundColor,
  517. ^(UIView *obj, CGFloat values[]) {
  518. POPUIColorGetRGBAComponents(obj.backgroundColor, values);
  519. },
  520. ^(UIView *obj, const CGFloat values[]) {
  521. obj.backgroundColor = POPUIColorRGBACreate(values);
  522. },
  523. kPOPThresholdColor
  524. },
  525. {kPOPViewCenter,
  526. ^(UIView *obj, CGFloat values[]) {
  527. values_from_point(values, obj.center);
  528. },
  529. ^(UIView *obj, const CGFloat values[]) {
  530. obj.center = values_to_point(values);
  531. },
  532. kPOPThresholdPoint
  533. },
  534. {kPOPViewFrame,
  535. ^(UIView *obj, CGFloat values[]) {
  536. values_from_rect(values, obj.frame);
  537. },
  538. ^(UIView *obj, const CGFloat values[]) {
  539. obj.frame = values_to_rect(values);
  540. },
  541. kPOPThresholdPoint
  542. },
  543. {kPOPViewScaleX,
  544. ^(UIView *obj, CGFloat values[]) {
  545. values[0] = POPLayerGetScaleX(obj.layer);
  546. },
  547. ^(UIView *obj, const CGFloat values[]) {
  548. POPLayerSetScaleX(obj.layer, values[0]);
  549. },
  550. kPOPThresholdScale
  551. },
  552. {kPOPViewScaleY,
  553. ^(UIView *obj, CGFloat values[]) {
  554. values[0] = POPLayerGetScaleY(obj.layer);
  555. },
  556. ^(UIView *obj, const CGFloat values[]) {
  557. POPLayerSetScaleY(obj.layer, values[0]);
  558. },
  559. kPOPThresholdScale
  560. },
  561. {kPOPViewScaleXY,
  562. ^(UIView *obj, CGFloat values[]) {
  563. values_from_point(values, POPLayerGetScaleXY(obj.layer));
  564. },
  565. ^(UIView *obj, const CGFloat values[]) {
  566. POPLayerSetScaleXY(obj.layer, values_to_point(values));
  567. },
  568. kPOPThresholdScale
  569. },
  570. {kPOPViewTintColor,
  571. ^(UIView *obj, CGFloat values[]) {
  572. POPUIColorGetRGBAComponents(obj.tintColor, values);
  573. },
  574. ^(UIView *obj, const CGFloat values[]) {
  575. obj.tintColor = POPUIColorRGBACreate(values);
  576. },
  577. kPOPThresholdColor
  578. },
  579. /* UIScrollView */
  580. {kPOPScrollViewContentOffset,
  581. ^(UIScrollView *obj, CGFloat values[]) {
  582. values_from_point(values, obj.contentOffset);
  583. },
  584. ^(UIScrollView *obj, const CGFloat values[]) {
  585. [obj setContentOffset:values_to_point(values) animated:NO];
  586. },
  587. kPOPThresholdPoint
  588. },
  589. {kPOPScrollViewContentSize,
  590. ^(UIScrollView *obj, CGFloat values[]) {
  591. values_from_size(values, obj.contentSize);
  592. },
  593. ^(UIScrollView *obj, const CGFloat values[]) {
  594. obj.contentSize = values_to_size(values);
  595. },
  596. kPOPThresholdPoint
  597. },
  598. {kPOPScrollViewZoomScale,
  599. ^(UIScrollView *obj, CGFloat values[]) {
  600. values[0]=obj.zoomScale;
  601. },
  602. ^(UIScrollView *obj, const CGFloat values[]) {
  603. obj.zoomScale=values[0];
  604. },
  605. kPOPThresholdScale
  606. },
  607. {kPOPScrollViewContentInset,
  608. ^(UIScrollView *obj, CGFloat values[]) {
  609. values[0] = obj.contentInset.top;
  610. values[1] = obj.contentInset.left;
  611. values[2] = obj.contentInset.bottom;
  612. values[3] = obj.contentInset.right;
  613. },
  614. ^(UIScrollView *obj, const CGFloat values[]) {
  615. obj.contentInset = values_to_edge_insets(values);
  616. },
  617. kPOPThresholdPoint
  618. },
  619. {kPOPScrollViewScrollIndicatorInsets,
  620. ^(UIScrollView *obj, CGFloat values[]) {
  621. values[0] = obj.scrollIndicatorInsets.top;
  622. values[1] = obj.scrollIndicatorInsets.left;
  623. values[2] = obj.scrollIndicatorInsets.bottom;
  624. values[3] = obj.scrollIndicatorInsets.right;
  625. },
  626. ^(UIScrollView *obj, const CGFloat values[]) {
  627. obj.scrollIndicatorInsets = values_to_edge_insets(values);
  628. },
  629. kPOPThresholdPoint
  630. },
  631. /* UINavigationBar */
  632. {kPOPNavigationBarBarTintColor,
  633. ^(UINavigationBar *obj, CGFloat values[]) {
  634. POPUIColorGetRGBAComponents(obj.barTintColor, values);
  635. },
  636. ^(UINavigationBar *obj, const CGFloat values[]) {
  637. obj.barTintColor = POPUIColorRGBACreate(values);
  638. },
  639. kPOPThresholdColor
  640. },
  641. /* UILabel */
  642. {kPOPLabelTextColor,
  643. ^(UILabel *obj, CGFloat values[]) {
  644. POPUIColorGetRGBAComponents(obj.textColor, values);
  645. },
  646. ^(UILabel *obj, const CGFloat values[]) {
  647. obj.textColor = POPUIColorRGBACreate(values);
  648. },
  649. kPOPThresholdColor
  650. },
  651. #else
  652. /* NSView */
  653. {kPOPViewFrame,
  654. ^(NSView *obj, CGFloat values[]) {
  655. values_from_rect(values, NSRectToCGRect(obj.frame));
  656. },
  657. ^(NSView *obj, const CGFloat values[]) {
  658. obj.frame = NSRectFromCGRect(values_to_rect(values));
  659. },
  660. kPOPThresholdPoint
  661. },
  662. {kPOPViewBounds,
  663. ^(NSView *obj, CGFloat values[]) {
  664. values_from_rect(values, NSRectToCGRect(obj.frame));
  665. },
  666. ^(NSView *obj, const CGFloat values[]) {
  667. obj.bounds = NSRectFromCGRect(values_to_rect(values));
  668. },
  669. kPOPThresholdPoint
  670. },
  671. {kPOPViewAlphaValue,
  672. ^(NSView *obj, CGFloat values[]) {
  673. values[0] = obj.alphaValue;
  674. },
  675. ^(NSView *obj, const CGFloat values[]) {
  676. obj.alphaValue = values[0];
  677. },
  678. kPOPThresholdOpacity
  679. },
  680. {kPOPViewFrameRotation,
  681. ^(NSView *obj, CGFloat values[]) {
  682. values[0] = obj.frameRotation;
  683. },
  684. ^(NSView *obj, const CGFloat values[]) {
  685. obj.frameRotation = values[0];
  686. },
  687. kPOPThresholdRotation
  688. },
  689. {kPOPViewFrameCenterRotation,
  690. ^(NSView *obj, CGFloat values[]) {
  691. values[0] = obj.frameCenterRotation;
  692. },
  693. ^(NSView *obj, const CGFloat values[]) {
  694. obj.frameCenterRotation = values[0];
  695. },
  696. kPOPThresholdRotation
  697. },
  698. {kPOPViewBoundsRotation,
  699. ^(NSView *obj, CGFloat values[]) {
  700. values[0] = obj.boundsRotation;
  701. },
  702. ^(NSView *obj, const CGFloat values[]) {
  703. obj.boundsRotation = values[0];
  704. },
  705. kPOPThresholdRotation
  706. },
  707. /* NSWindow */
  708. {kPOPWindowFrame,
  709. ^(NSWindow *obj, CGFloat values[]) {
  710. values_from_rect(values, NSRectToCGRect(obj.frame));
  711. },
  712. ^(NSWindow *obj, const CGFloat values[]) {
  713. [obj setFrame:NSRectFromCGRect(values_to_rect(values)) display:YES];
  714. },
  715. kPOPThresholdPoint
  716. },
  717. {kPOPWindowAlphaValue,
  718. ^(NSWindow *obj, CGFloat values[]) {
  719. values[0] = obj.alphaValue;
  720. },
  721. ^(NSWindow *obj, const CGFloat values[]) {
  722. obj.alphaValue = values[0];
  723. },
  724. kPOPThresholdOpacity
  725. },
  726. {kPOPWindowBackgroundColor,
  727. ^(NSWindow *obj, CGFloat values[]) {
  728. POPNSColorGetRGBAComponents(obj.backgroundColor, values);
  729. },
  730. ^(NSWindow *obj, const CGFloat values[]) {
  731. obj.backgroundColor = POPNSColorRGBACreate(values);
  732. },
  733. kPOPThresholdColor
  734. },
  735. #endif
  736. #if SCENEKIT_SDK_AVAILABLE
  737. /* SceneKit */
  738. {kPOPSCNNodePosition,
  739. ^(SCNNode *obj, CGFloat values[]) {
  740. values_from_vec3(values, obj.position);
  741. },
  742. ^(SCNNode *obj, const CGFloat values[]) {
  743. obj.position = values_to_vec3(values);
  744. },
  745. kPOPThresholdScale
  746. },
  747. {kPOPSCNNodePositionX,
  748. ^(SCNNode *obj, CGFloat values[]) {
  749. values[0] = obj.position.x;
  750. },
  751. ^(SCNNode *obj, const CGFloat values[]) {
  752. obj.position = SCNVector3Make(values[0], obj.position.y, obj.position.z);
  753. },
  754. kPOPThresholdScale
  755. },
  756. {kPOPSCNNodePositionY,
  757. ^(SCNNode *obj, CGFloat values[]) {
  758. values[0] = obj.position.y;
  759. },
  760. ^(SCNNode *obj, const CGFloat values[]) {
  761. obj.position = SCNVector3Make(obj.position.x, values[0], obj.position.z);
  762. },
  763. kPOPThresholdScale
  764. },
  765. {kPOPSCNNodePositionZ,
  766. ^(SCNNode *obj, CGFloat values[]) {
  767. values[0] = obj.position.z;
  768. },
  769. ^(SCNNode *obj, const CGFloat values[]) {
  770. obj.position = SCNVector3Make(obj.position.x, obj.position.y, values[0]);
  771. },
  772. kPOPThresholdScale
  773. },
  774. {kPOPSCNNodeTranslation,
  775. ^(SCNNode *obj, CGFloat values[]) {
  776. values[0] = obj.transform.m41;
  777. values[1] = obj.transform.m42;
  778. values[2] = obj.transform.m43;
  779. },
  780. ^(SCNNode *obj, const CGFloat values[]) {
  781. obj.transform = SCNMatrix4MakeTranslation(values[0], values[1], values[2]);
  782. },
  783. kPOPThresholdScale
  784. },
  785. {kPOPSCNNodeTranslationX,
  786. ^(SCNNode *obj, CGFloat values[]) {
  787. values[0] = obj.transform.m41;
  788. },
  789. ^(SCNNode *obj, const CGFloat values[]) {
  790. obj.transform = SCNMatrix4MakeTranslation(values[0], obj.transform.m42, obj.transform.m43);
  791. },
  792. kPOPThresholdScale
  793. },
  794. {kPOPSCNNodeTranslationY,
  795. ^(SCNNode *obj, CGFloat values[]) {
  796. values[0] = obj.transform.m42;
  797. },
  798. ^(SCNNode *obj, const CGFloat values[]) {
  799. obj.transform = SCNMatrix4MakeTranslation(obj.transform.m41, values[0], obj.transform.m43);
  800. },
  801. kPOPThresholdScale
  802. },
  803. {kPOPSCNNodeTranslationY,
  804. ^(SCNNode *obj, CGFloat values[]) {
  805. values[0] = obj.transform.m43;
  806. },
  807. ^(SCNNode *obj, const CGFloat values[]) {
  808. obj.transform = SCNMatrix4MakeTranslation(obj.transform.m41, obj.transform.m42, values[0]);
  809. },
  810. kPOPThresholdScale
  811. },
  812. {kPOPSCNNodeRotation,
  813. ^(SCNNode *obj, CGFloat values[]) {
  814. values_from_vec4(values, obj.rotation);
  815. },
  816. ^(SCNNode *obj, const CGFloat values[]) {
  817. obj.rotation = values_to_vec4(values);
  818. },
  819. kPOPThresholdScale
  820. },
  821. {kPOPSCNNodeRotationX,
  822. ^(SCNNode *obj, CGFloat values[]) {
  823. values[0] = obj.rotation.x;
  824. },
  825. ^(SCNNode *obj, const CGFloat values[]) {
  826. obj.rotation = SCNVector4Make(1.0, obj.rotation.y, obj.rotation.z, values[0]);
  827. },
  828. kPOPThresholdScale
  829. },
  830. {kPOPSCNNodeRotationY,
  831. ^(SCNNode *obj, CGFloat values[]) {
  832. values[0] = obj.rotation.y;
  833. },
  834. ^(SCNNode *obj, const CGFloat values[]) {
  835. obj.rotation = SCNVector4Make(obj.rotation.x, 1.0, obj.rotation.z, values[0]);
  836. },
  837. kPOPThresholdScale
  838. },
  839. {kPOPSCNNodeRotationZ,
  840. ^(SCNNode *obj, CGFloat values[]) {
  841. values[0] = obj.rotation.z;
  842. },
  843. ^(SCNNode *obj, const CGFloat values[]) {
  844. obj.rotation = SCNVector4Make(obj.rotation.x, obj.rotation.y, 1.0, values[0]);
  845. },
  846. kPOPThresholdScale
  847. },
  848. {kPOPSCNNodeRotationW,
  849. ^(SCNNode *obj, CGFloat values[]) {
  850. values[0] = obj.rotation.w;
  851. },
  852. ^(SCNNode *obj, const CGFloat values[]) {
  853. obj.rotation = SCNVector4Make(obj.rotation.x, obj.rotation.y, obj.rotation.z, values[0]);
  854. },
  855. kPOPThresholdScale
  856. },
  857. {kPOPSCNNodeEulerAngles,
  858. ^(SCNNode *obj, CGFloat values[]) {
  859. values_from_vec3(values, obj.eulerAngles);
  860. },
  861. ^(SCNNode *obj, const CGFloat values[]) {
  862. obj.eulerAngles = values_to_vec3(values);
  863. },
  864. kPOPThresholdScale
  865. },
  866. {kPOPSCNNodeEulerAnglesX,
  867. ^(SCNNode *obj, CGFloat values[]) {
  868. values[0] = obj.eulerAngles.x;
  869. },
  870. ^(SCNNode *obj, const CGFloat values[]) {
  871. obj.eulerAngles = SCNVector3Make(values[0], obj.eulerAngles.y, obj.eulerAngles.z);
  872. },
  873. kPOPThresholdScale
  874. },
  875. {kPOPSCNNodeEulerAnglesY,
  876. ^(SCNNode *obj, CGFloat values[]) {
  877. values[0] = obj.eulerAngles.y;
  878. },
  879. ^(SCNNode *obj, const CGFloat values[]) {
  880. obj.eulerAngles = SCNVector3Make(obj.eulerAngles.x, values[0], obj.eulerAngles.z);
  881. },
  882. kPOPThresholdScale
  883. },
  884. {kPOPSCNNodeEulerAnglesZ,
  885. ^(SCNNode *obj, CGFloat values[]) {
  886. values[0] = obj.eulerAngles.z;
  887. },
  888. ^(SCNNode *obj, const CGFloat values[]) {
  889. obj.eulerAngles = SCNVector3Make(obj.eulerAngles.x, obj.eulerAngles.y, values[0]);
  890. },
  891. kPOPThresholdScale
  892. },
  893. {kPOPSCNNodeOrientation,
  894. ^(SCNNode *obj, CGFloat values[]) {
  895. values_from_vec4(values, obj.orientation);
  896. },
  897. ^(SCNNode *obj, const CGFloat values[]) {
  898. obj.orientation = values_to_vec4(values);
  899. },
  900. kPOPThresholdScale
  901. },
  902. {kPOPSCNNodeOrientationX,
  903. ^(SCNNode *obj, CGFloat values[]) {
  904. values[0] = obj.orientation.x;
  905. },
  906. ^(SCNNode *obj, const CGFloat values[]) {
  907. obj.orientation = SCNVector4Make(values[0], obj.orientation.y, obj.orientation.z, obj.orientation.w);
  908. },
  909. kPOPThresholdScale
  910. },
  911. {kPOPSCNNodeOrientationY,
  912. ^(SCNNode *obj, CGFloat values[]) {
  913. values[0] = obj.orientation.y;
  914. },
  915. ^(SCNNode *obj, const CGFloat values[]) {
  916. obj.orientation = SCNVector4Make(obj.orientation.x, values[0], obj.orientation.z, obj.orientation.w);
  917. },
  918. kPOPThresholdScale
  919. },
  920. {kPOPSCNNodeOrientationZ,
  921. ^(SCNNode *obj, CGFloat values[]) {
  922. values[0] = obj.orientation.z;
  923. },
  924. ^(SCNNode *obj, const CGFloat values[]) {
  925. obj.orientation = SCNVector4Make(obj.orientation.x, obj.orientation.y, values[0], obj.orientation.w);
  926. },
  927. kPOPThresholdScale
  928. },
  929. {kPOPSCNNodeOrientationW,
  930. ^(SCNNode *obj, CGFloat values[]) {
  931. values[0] = obj.orientation.w;
  932. },
  933. ^(SCNNode *obj, const CGFloat values[]) {
  934. obj.orientation = SCNVector4Make(obj.orientation.x, obj.orientation.y, obj.orientation.z, values[0]);
  935. },
  936. kPOPThresholdScale
  937. },
  938. {kPOPSCNNodeScale,
  939. ^(SCNNode *obj, CGFloat values[]) {
  940. values_from_vec3(values, obj.scale);
  941. },
  942. ^(SCNNode *obj, const CGFloat values[]) {
  943. obj.scale = values_to_vec3(values);
  944. },
  945. kPOPThresholdScale
  946. },
  947. {kPOPSCNNodeScaleX,
  948. ^(SCNNode *obj, CGFloat values[]) {
  949. values[0] = obj.scale.x;
  950. },
  951. ^(SCNNode *obj, const CGFloat values[]) {
  952. obj.scale = SCNVector3Make(values[0], obj.scale.y, obj.scale.z);
  953. },
  954. kPOPThresholdScale
  955. },
  956. {kPOPSCNNodeScaleY,
  957. ^(SCNNode *obj, CGFloat values[]) {
  958. values[0] = obj.scale.y;
  959. },
  960. ^(SCNNode *obj, const CGFloat values[]) {
  961. obj.position = SCNVector3Make(obj.scale.x, values[0], obj.scale.z);
  962. },
  963. kPOPThresholdScale
  964. },
  965. {kPOPSCNNodeScaleZ,
  966. ^(SCNNode *obj, CGFloat values[]) {
  967. values[0] = obj.scale.z;
  968. },
  969. ^(SCNNode *obj, const CGFloat values[]) {
  970. obj.scale = SCNVector3Make(obj.scale.x, obj.scale.y, values[0]);
  971. },
  972. kPOPThresholdScale
  973. },
  974. {kPOPSCNNodeScaleXY,
  975. ^(SCNNode *obj, CGFloat values[]) {
  976. values[0] = obj.scale.x;
  977. values[1] = obj.scale.y;
  978. },
  979. ^(SCNNode *obj, const CGFloat values[]) {
  980. obj.scale = SCNVector3Make(values[0], values[1], obj.scale.z);
  981. },
  982. kPOPThresholdScale
  983. },
  984. #endif
  985. };
  986. static NSUInteger staticIndexWithName(NSString *aName)
  987. {
  988. NSUInteger idx = 0;
  989. while (idx < POP_ARRAY_COUNT(_staticStates)) {
  990. if ([_staticStates[idx].name isEqualToString:aName])
  991. return idx;
  992. idx++;
  993. }
  994. return NSNotFound;
  995. }
  996. /**
  997. Concrete static property class.
  998. */
  999. @interface POPStaticAnimatableProperty : POPAnimatableProperty
  1000. {
  1001. @public
  1002. POPStaticAnimatablePropertyState *_state;
  1003. }
  1004. @end
  1005. @implementation POPStaticAnimatableProperty
  1006. - (NSString *)name
  1007. {
  1008. return _state->name;
  1009. }
  1010. - (pop_animatable_read_block)readBlock
  1011. {
  1012. return _state->readBlock;
  1013. }
  1014. - (pop_animatable_write_block)writeBlock
  1015. {
  1016. return _state->writeBlock;
  1017. }
  1018. - (CGFloat)threshold
  1019. {
  1020. return _state->threshold;
  1021. }
  1022. @end
  1023. #pragma mark - Concrete
  1024. /**
  1025. Concrete immutable property class.
  1026. */
  1027. @interface POPConcreteAnimatableProperty : POPAnimatableProperty
  1028. - (instancetype)initWithName:(NSString *)name readBlock:(pop_animatable_read_block)read writeBlock:(pop_animatable_write_block)write threshold:(CGFloat)threshold;
  1029. @end
  1030. @implementation POPConcreteAnimatableProperty
  1031. // default synthesis
  1032. @synthesize name, readBlock, writeBlock, threshold;
  1033. - (instancetype)initWithName:(NSString *)aName readBlock:(pop_animatable_read_block)aReadBlock writeBlock:(pop_animatable_write_block)aWriteBlock threshold:(CGFloat)aThreshold
  1034. {
  1035. self = [super init];
  1036. if (nil != self) {
  1037. name = [aName copy];
  1038. readBlock = [aReadBlock copy];
  1039. writeBlock = [aWriteBlock copy];
  1040. threshold = aThreshold;
  1041. }
  1042. return self;
  1043. }
  1044. @end
  1045. #pragma mark - Mutable
  1046. @implementation POPMutableAnimatableProperty
  1047. // default synthesis
  1048. @synthesize name, readBlock, writeBlock, threshold;
  1049. @end
  1050. #pragma mark - Cluster
  1051. /**
  1052. Singleton placeholder property class to support class cluster.
  1053. */
  1054. @interface POPPlaceholderAnimatableProperty : POPAnimatableProperty
  1055. @end
  1056. @implementation POPPlaceholderAnimatableProperty
  1057. // default synthesis
  1058. @synthesize name, readBlock, writeBlock, threshold;
  1059. @end
  1060. /**
  1061. Cluster class.
  1062. */
  1063. @implementation POPAnimatableProperty
  1064. // avoid creating backing ivars
  1065. @dynamic name, readBlock, writeBlock, threshold;
  1066. static POPAnimatableProperty *placeholder = nil;
  1067. + (void)initialize
  1068. {
  1069. if (self == [POPAnimatableProperty class]) {
  1070. placeholder = [POPPlaceholderAnimatableProperty alloc];
  1071. }
  1072. }
  1073. + (id)allocWithZone:(struct _NSZone *)zone
  1074. {
  1075. if (self == [POPAnimatableProperty class]) {
  1076. if (nil == placeholder) {
  1077. placeholder = [super allocWithZone:zone];
  1078. }
  1079. return placeholder;
  1080. }
  1081. return [super allocWithZone:zone];
  1082. }
  1083. - (id)copyWithZone:(NSZone *)zone
  1084. {
  1085. if ([self isKindOfClass:[POPMutableAnimatableProperty class]]) {
  1086. POPConcreteAnimatableProperty *copyProperty = [[POPConcreteAnimatableProperty alloc] initWithName:self.name readBlock:self.readBlock writeBlock:self.writeBlock threshold:self.threshold];
  1087. return copyProperty;
  1088. } else {
  1089. return self;
  1090. }
  1091. }
  1092. - (id)mutableCopyWithZone:(NSZone *)zone
  1093. {
  1094. POPMutableAnimatableProperty *copyProperty = [[POPMutableAnimatableProperty alloc] init];
  1095. copyProperty.name = self.name;
  1096. copyProperty.readBlock = self.readBlock;
  1097. copyProperty.writeBlock = self.writeBlock;
  1098. copyProperty.threshold = self.threshold;
  1099. return copyProperty;
  1100. }
  1101. + (id)propertyWithName:(NSString *)aName
  1102. {
  1103. return [self propertyWithName:aName initializer:NULL];
  1104. }
  1105. + (id)propertyWithName:(NSString *)aName initializer:(void (^)(POPMutableAnimatableProperty *prop))aBlock
  1106. {
  1107. POPAnimatableProperty *prop = nil;
  1108. static NSMutableDictionary *_propertyDict = nil;
  1109. if (nil == _propertyDict) {
  1110. _propertyDict = [[NSMutableDictionary alloc] initWithCapacity:10];
  1111. }
  1112. prop = _propertyDict[aName];
  1113. if (nil != prop) {
  1114. return prop;
  1115. }
  1116. NSUInteger staticIdx = staticIndexWithName(aName);
  1117. if (NSNotFound != staticIdx) {
  1118. POPStaticAnimatableProperty *staticProp = [[POPStaticAnimatableProperty alloc] init];
  1119. staticProp->_state = &_staticStates[staticIdx];
  1120. _propertyDict[aName] = staticProp;
  1121. prop = staticProp;
  1122. } else if (NULL != aBlock) {
  1123. POPMutableAnimatableProperty *mutableProp = [[POPMutableAnimatableProperty alloc] init];
  1124. mutableProp.name = aName;
  1125. mutableProp.threshold = 1.0;
  1126. aBlock(mutableProp);
  1127. prop = [mutableProp copy];
  1128. }
  1129. return prop;
  1130. }
  1131. - (NSString *)description
  1132. {
  1133. NSMutableString *s = [NSMutableString stringWithFormat:@"%@ name:%@ threshold:%f", super.description, self.name, self.threshold];
  1134. return s;
  1135. }
  1136. @end