POPAnimatableProperty.mm 35 KB

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