ASDisplayNode.mm 146 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175
  1. //
  2. // ASDisplayNode.mm
  3. // AsyncDisplayKit
  4. //
  5. // Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
  6. // This source code is licensed under the BSD-style license found in the
  7. // LICENSE file in the root directory of this source tree. An additional grant
  8. // of patent rights can be found in the PATENTS file in the same directory.
  9. //
  10. #import <AsyncDisplayKit/ASDisplayNodeInternal.h>
  11. #import <AsyncDisplayKit/ASDisplayNode+FrameworkSubclasses.h>
  12. #import <AsyncDisplayKit/ASDisplayNode+Beta.h>
  13. #import <AsyncDisplayKit/ASDisplayNode+Deprecated.h>
  14. #import <AsyncDisplayKit/AsyncDisplayKit+Debug.h>
  15. #import <AsyncDisplayKit/ASLayoutSpec+Subclasses.h>
  16. #import <AsyncDisplayKit/ASCellNode+Internal.h>
  17. #import <objc/runtime.h>
  18. #import <AsyncDisplayKit/_ASAsyncTransaction.h>
  19. #import <AsyncDisplayKit/_ASAsyncTransactionContainer+Private.h>
  20. #import <AsyncDisplayKit/_ASCoreAnimationExtras.h>
  21. #import <AsyncDisplayKit/_ASDisplayLayer.h>
  22. #import <AsyncDisplayKit/_ASDisplayView.h>
  23. #import <AsyncDisplayKit/_ASPendingState.h>
  24. #import <AsyncDisplayKit/_ASScopeTimer.h>
  25. #import <AsyncDisplayKit/ASDimension.h>
  26. #import <AsyncDisplayKit/ASDisplayNodeExtras.h>
  27. #import <AsyncDisplayKit/ASEqualityHelpers.h>
  28. #import <AsyncDisplayKit/ASInternalHelpers.h>
  29. #import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
  30. #import <AsyncDisplayKit/ASLayoutSpec.h>
  31. #import <AsyncDisplayKit/ASLayoutSpecPrivate.h>
  32. #import <AsyncDisplayKit/ASRunLoopQueue.h>
  33. #import <AsyncDisplayKit/ASTraitCollection.h>
  34. #import <AsyncDisplayKit/ASWeakProxy.h>
  35. #import <AsyncDisplayKit/ASResponderChainEnumerator.h>
  36. /**
  37. * Assert if the current thread owns a mutex.
  38. * This assertion is useful when you want to indicate and enforce the locking policy/expectation of methods.
  39. * To determine when and which methods acquired a (recursive) mutex (to debug deadlocks, for example),
  40. * put breakpoints at some of these assertions. When the breakpoints hit, walk through stack trace frames
  41. * and check ownership count of the mutex.
  42. */
  43. #if CHECK_LOCKING_SAFETY
  44. #define ASDisplayNodeAssertLockUnownedByCurrentThread(lock) ASDisplayNodeAssertFalse(lock.ownedByCurrentThread());
  45. #else
  46. #define ASDisplayNodeAssertLockUnownedByCurrentThread(lock)
  47. #endif
  48. #if ASDisplayNodeLoggingEnabled
  49. #define LOG(...) NSLog(__VA_ARGS__)
  50. #else
  51. #define LOG(...)
  52. #endif
  53. // Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
  54. #if TIME_DISPLAYNODE_OPS
  55. #define TIME_SCOPED(outVar) ASDN::ScopeTimer t(outVar)
  56. #else
  57. #define TIME_SCOPED(outVar)
  58. #endif
  59. static ASDisplayNodeNonFatalErrorBlock _nonFatalErrorBlock = nil;
  60. // Forward declare CALayerDelegate protocol as the iOS 10 SDK moves CALayerDelegate from a formal delegate to a protocol.
  61. // We have to forward declare the protocol as this place otherwise it will not compile compiling with an Base SDK < iOS 10
  62. @protocol CALayerDelegate;
  63. @interface ASDisplayNode () <UIGestureRecognizerDelegate, _ASDisplayLayerDelegate, _ASTransitionContextCompletionDelegate>
  64. /**
  65. * See ASDisplayNodeInternal.h for ivars
  66. */
  67. @end
  68. @implementation ASDisplayNode
  69. @dynamic layoutElementType;
  70. @synthesize debugName = _debugName;
  71. @synthesize threadSafeBounds = _threadSafeBounds;
  72. @synthesize layoutSpecBlock = _layoutSpecBlock;
  73. static BOOL suppressesInvalidCollectionUpdateExceptions = NO;
  74. + (BOOL)suppressesInvalidCollectionUpdateExceptions
  75. {
  76. return suppressesInvalidCollectionUpdateExceptions;
  77. }
  78. + (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses
  79. {
  80. suppressesInvalidCollectionUpdateExceptions = suppresses;
  81. }
  82. BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
  83. {
  84. return ASSubclassOverridesSelector([ASDisplayNode class], subclass, selector);
  85. }
  86. // For classes like ASTableNode, ASCollectionNode, ASScrollNode and similar - we have to be sure to set certain properties
  87. // like setFrame: and setBackgroundColor: directly to the UIView and not apply it to the layer only.
  88. BOOL ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(ASDisplayNodeFlags flags)
  89. {
  90. return flags.synchronous && !flags.layerBacked;
  91. }
  92. _ASPendingState *ASDisplayNodeGetPendingState(ASDisplayNode *node)
  93. {
  94. ASDN::MutexLocker l(node->__instanceLock__);
  95. _ASPendingState *result = node->_pendingViewState;
  96. if (result == nil) {
  97. result = [[_ASPendingState alloc] init];
  98. node->_pendingViewState = result;
  99. }
  100. return result;
  101. }
  102. /**
  103. * Returns ASDisplayNodeFlags for the given class/instance. instance MAY BE NIL.
  104. *
  105. * @param c the class, required
  106. * @param instance the instance, which may be nil. (If so, the class is inspected instead)
  107. * @remarks The instance value is used only if we suspect the class may be dynamic (because it overloads
  108. * +respondsToSelector: or -respondsToSelector.) In that case we use our "slow path", calling this
  109. * method on each -init and passing the instance value. While this may seem like an unlikely scenario,
  110. * it turns our our own internal tests use a dynamic class, so it's worth capturing this edge case.
  111. *
  112. * @return ASDisplayNode flags.
  113. */
  114. static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *instance)
  115. {
  116. ASDisplayNodeCAssertNotNil(c, @"class is required");
  117. struct ASDisplayNodeFlags flags = {0};
  118. flags.isInHierarchy = NO;
  119. flags.displaysAsynchronously = YES;
  120. flags.shouldAnimateSizeChanges = YES;
  121. flags.implementsDrawRect = ([c respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
  122. flags.implementsImageDisplay = ([c respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
  123. if (instance) {
  124. flags.implementsDrawParameters = ([instance respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
  125. flags.implementsInstanceDrawRect = ([instance respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
  126. flags.implementsInstanceImageDisplay = ([instance respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
  127. } else {
  128. flags.implementsDrawParameters = ([c instancesRespondToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0);
  129. flags.implementsInstanceDrawRect = ([c instancesRespondToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0);
  130. flags.implementsInstanceImageDisplay = ([c instancesRespondToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0);
  131. }
  132. return flags;
  133. }
  134. /**
  135. * Returns ASDisplayNodeMethodOverrides for the given class
  136. *
  137. * @param c the class, required.
  138. *
  139. * @return ASDisplayNodeMethodOverrides.
  140. */
  141. static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
  142. {
  143. ASDisplayNodeCAssertNotNil(c, @"class is required");
  144. ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone;
  145. if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesBegan:withEvent:))) {
  146. overrides |= ASDisplayNodeMethodOverrideTouchesBegan;
  147. }
  148. if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesMoved:withEvent:))) {
  149. overrides |= ASDisplayNodeMethodOverrideTouchesMoved;
  150. }
  151. if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesCancelled:withEvent:))) {
  152. overrides |= ASDisplayNodeMethodOverrideTouchesCancelled;
  153. }
  154. if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesEnded:withEvent:))) {
  155. overrides |= ASDisplayNodeMethodOverrideTouchesEnded;
  156. }
  157. if (ASDisplayNodeSubclassOverridesSelector(c, @selector(layoutSpecThatFits:))) {
  158. overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits;
  159. }
  160. if (ASDisplayNodeSubclassOverridesSelector(c, @selector(fetchData))) {
  161. overrides |= ASDisplayNodeMethodOverrideFetchData;
  162. }
  163. if (ASDisplayNodeSubclassOverridesSelector(c, @selector(clearFetchedData))) {
  164. overrides |= ASDisplayNodeMethodOverrideClearFetchedData;
  165. }
  166. return overrides;
  167. }
  168. + (void)initialize
  169. {
  170. [super initialize];
  171. if (self != [ASDisplayNode class]) {
  172. // Subclasses should never override these. Use unused to prevent warnings
  173. __unused NSString *classString = NSStringFromClass(self);
  174. ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method.", classString);
  175. ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method.", classString);
  176. ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure: method", classString);
  177. ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead override calculateLayoutThatFits:", classString);
  178. ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method. Instead override calculateLayoutThatFits:.", classString);
  179. ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead override calculateLayoutThatFits:.", classString);
  180. ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method.", classString);
  181. ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearPreloadedData)), @"Subclass %@ must not override recursivelyClearFetchedData method.", classString);
  182. }
  183. // Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values
  184. // when each instance is constructed. These values don't change for each class, so there is significant performance benefit
  185. // in doing it here. +initialize is guaranteed to be called before any instance method so it is safe to add this method here.
  186. // Note that we take care to detect if the class overrides +respondsToSelector: or -respondsToSelector and take the slow path
  187. // (recalculating for each instance) to make sure we are always correct.
  188. BOOL classOverridesRespondsToSelector = ASSubclassOverridesClassSelector([NSObject class], self, @selector(respondsToSelector:));
  189. BOOL instancesOverrideRespondsToSelector = ASSubclassOverridesSelector([NSObject class], self, @selector(respondsToSelector:));
  190. struct ASDisplayNodeFlags flags = GetASDisplayNodeFlags(self, nil);
  191. ASDisplayNodeMethodOverrides methodOverrides = GetASDisplayNodeMethodOverrides(self);
  192. __unused Class initializeSelf = self;
  193. IMP staticInitialize = imp_implementationWithBlock(^(ASDisplayNode *node) {
  194. ASDisplayNodeAssert(node.class == initializeSelf, @"Node class %@ does not have a matching _staticInitialize method; check to ensure [super initialize] is called within any custom +initialize implementations! Overridden methods will not be called unless they are also implemented by superclass %@", node.class, initializeSelf);
  195. node->_flags = (classOverridesRespondsToSelector || instancesOverrideRespondsToSelector) ? GetASDisplayNodeFlags(node.class, node) : flags;
  196. node->_methodOverrides = (classOverridesRespondsToSelector) ? GetASDisplayNodeMethodOverrides(node.class) : methodOverrides;
  197. });
  198. class_replaceMethod(self, @selector(_staticInitialize), staticInitialize, "v:@");
  199. #if DEBUG
  200. // Check if subnodes where modified during the creation of the layout
  201. if (self == [ASDisplayNode class]) {
  202. __block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) {
  203. NSArray *oldSubnodes = _self.subnodes;
  204. ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_layoutElementThatFits:), sizeRange);
  205. NSArray *subnodes = _self.subnodes;
  206. ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior.");
  207. for (NSInteger i = 0; i < oldSubnodes.count; i++) {
  208. ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior.");
  209. }
  210. return layoutElement;
  211. });
  212. }
  213. #endif
  214. }
  215. + (void)load
  216. {
  217. // Ensure this value is cached on the main thread before needed in the background.
  218. ASScreenScale();
  219. }
  220. + (BOOL)layerBackedNodesEnabled
  221. {
  222. return YES;
  223. }
  224. + (Class)viewClass
  225. {
  226. return [_ASDisplayView class];
  227. }
  228. + (Class)layerClass
  229. {
  230. return [_ASDisplayLayer class];
  231. }
  232. #pragma mark - Lifecycle
  233. - (void)_staticInitialize
  234. {
  235. ASDisplayNodeAssert(NO, @"_staticInitialize must be overridden");
  236. }
  237. - (void)_initializeInstance
  238. {
  239. [self _staticInitialize];
  240. #if ASEVENTLOG_ENABLE
  241. _eventLog = [[ASEventLog alloc] initWithObject:self];
  242. #endif
  243. _contentsScaleForDisplay = ASScreenScale();
  244. _primitiveTraitCollection = ASPrimitiveTraitCollectionMakeDefault();
  245. _calculatedDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>();
  246. _pendingDisplayNodeLayout = nullptr;
  247. _defaultLayoutTransitionDuration = 0.2;
  248. _defaultLayoutTransitionDelay = 0.0;
  249. _defaultLayoutTransitionOptions = UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionNone;
  250. _flags.canClearContentsOfLayer = YES;
  251. _flags.canCallSetNeedsDisplayOfLayer = YES;
  252. ASDisplayNodeLogEvent(self, @"init");
  253. }
  254. - (instancetype)init
  255. {
  256. if (!(self = [super init]))
  257. return nil;
  258. [self _initializeInstance];
  259. return self;
  260. }
  261. - (instancetype)initWithViewClass:(Class)viewClass
  262. {
  263. if (!(self = [super init]))
  264. return nil;
  265. ASDisplayNodeAssert([viewClass isSubclassOfClass:[UIView class]], @"should initialize with a subclass of UIView");
  266. [self _initializeInstance];
  267. _viewClass = viewClass;
  268. _flags.synchronous = ![viewClass isSubclassOfClass:[_ASDisplayView class]];
  269. return self;
  270. }
  271. - (instancetype)initWithLayerClass:(Class)layerClass
  272. {
  273. if (!(self = [super init]))
  274. return nil;
  275. ASDisplayNodeAssert([layerClass isSubclassOfClass:[CALayer class]], @"should initialize with a subclass of CALayer");
  276. [self _initializeInstance];
  277. _layerClass = layerClass;
  278. _flags.synchronous = ![layerClass isSubclassOfClass:[_ASDisplayLayer class]];
  279. _flags.layerBacked = YES;
  280. return self;
  281. }
  282. - (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock
  283. {
  284. return [self initWithViewBlock:viewBlock didLoadBlock:nil];
  285. }
  286. - (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
  287. {
  288. if (!(self = [super init]))
  289. return nil;
  290. ASDisplayNodeAssertNotNil(viewBlock, @"should initialize with a valid block that returns a UIView");
  291. [self _initializeInstance];
  292. _viewBlock = viewBlock;
  293. _flags.synchronous = YES;
  294. if (didLoadBlock != nil) {
  295. _onDidLoadBlocks = [NSMutableArray arrayWithObject:didLoadBlock];
  296. }
  297. return self;
  298. }
  299. - (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock
  300. {
  301. return [self initWithLayerBlock:layerBlock didLoadBlock:nil];
  302. }
  303. - (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
  304. {
  305. if (!(self = [super init]))
  306. return nil;
  307. ASDisplayNodeAssertNotNil(layerBlock, @"should initialize with a valid block that returns a CALayer");
  308. [self _initializeInstance];
  309. _layerBlock = layerBlock;
  310. _flags.synchronous = YES;
  311. _flags.layerBacked = YES;
  312. if (didLoadBlock != nil) {
  313. _onDidLoadBlocks = [NSMutableArray arrayWithObject:didLoadBlock];
  314. }
  315. return self;
  316. }
  317. - (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body
  318. {
  319. ASDN::MutexLocker l(__instanceLock__);
  320. if ([self _isNodeLoaded]) {
  321. ASDisplayNodeAssertThreadAffinity(self);
  322. ASDN::MutexUnlocker l(__instanceLock__);
  323. body(self);
  324. } else if (_onDidLoadBlocks == nil) {
  325. _onDidLoadBlocks = [NSMutableArray arrayWithObject:body];
  326. } else {
  327. [_onDidLoadBlocks addObject:body];
  328. }
  329. }
  330. - (void)dealloc
  331. {
  332. _flags.isDeallocating = YES;
  333. // Synchronous nodes may not be able to call the hierarchy notifications, so only enforce for regular nodes.
  334. ASDisplayNodeAssert(_flags.synchronous || !ASInterfaceStateIncludesVisible(_interfaceState), @"Node should always be marked invisible before deallocating. Node: %@", self);
  335. self.asyncLayer.asyncDelegate = nil;
  336. _view.asyncdisplaykit_node = nil;
  337. _layer.asyncdisplaykit_node = nil;
  338. // Remove any subnodes so they lose their connection to the now deallocated parent. This can happen
  339. // because subnodes do not retain their supernode, but subnodes can legitimately remain alive if another
  340. // thing outside the view hierarchy system (e.g. async display, controller code, etc). keeps a retained
  341. // reference to subnodes.
  342. for (ASDisplayNode *subnode in _subnodes)
  343. [subnode __setSupernode:nil];
  344. // Trampoline any UIKit ivars' deallocation to main
  345. if (ASDisplayNodeThreadIsMain() == NO) {
  346. [self _scheduleIvarsForMainDeallocation];
  347. }
  348. _subnodes = nil;
  349. #if YOGA
  350. if (_yogaNode != NULL) {
  351. YGNodeFree(_yogaNode);
  352. }
  353. #endif
  354. // TODO: Remove this? If supernode isn't already nil, this method isn't dealloc-safe anyway.
  355. [self __setSupernode:nil];
  356. }
  357. - (void)_scheduleIvarsForMainDeallocation
  358. {
  359. NSValue *ivarsObj = [[self class] _ivarsThatMayNeedMainDeallocation];
  360. // Unwrap the ivar array
  361. unsigned int count = 0;
  362. // Will be unused if assertions are disabled.
  363. __unused int scanResult = sscanf(ivarsObj.objCType, "[%u^{objc_ivar}]", &count);
  364. ASDisplayNodeAssert(scanResult == 1, @"Unexpected type in NSValue: %s", ivarsObj.objCType);
  365. Ivar ivars[count];
  366. [ivarsObj getValue:ivars];
  367. for (Ivar ivar : ivars) {
  368. id value = object_getIvar(self, ivar);
  369. if (ASClassRequiresMainThreadDeallocation(object_getClass(value))) {
  370. LOG(@"Trampolining ivar '%s' value %@ for main deallocation.", ivar_getName(ivar), value);
  371. ASPerformMainThreadDeallocation(value);
  372. } else {
  373. LOG(@"Not trampolining ivar '%s' value %@.", ivar_getName(ivar), value);
  374. }
  375. }
  376. }
  377. /**
  378. * Returns an NSValue-wrapped array of all the ivars in this class or its superclasses
  379. * up through ASDisplayNode, that we expect may need to be deallocated on main.
  380. *
  381. * This method caches its results.
  382. */
  383. + (NSValue/*<[Ivar]>*/ * _Nonnull)_ivarsThatMayNeedMainDeallocation
  384. {
  385. static NSCache<Class, NSValue *> *ivarsCache;
  386. static dispatch_once_t onceToken;
  387. dispatch_once(&onceToken, ^{
  388. ivarsCache = [[NSCache alloc] init];
  389. });
  390. NSValue *result = [ivarsCache objectForKey:self];
  391. if (result != nil) {
  392. return result;
  393. }
  394. // Cache miss.
  395. unsigned int resultCount = 0;
  396. static const int kMaxDealloc2MainIvarsPerClassTree = 64;
  397. Ivar resultIvars[kMaxDealloc2MainIvarsPerClassTree];
  398. // Get superclass results first.
  399. Class c = class_getSuperclass(self);
  400. if (c != [NSObject class]) {
  401. NSValue *ivarsObj = [c _ivarsThatMayNeedMainDeallocation];
  402. // Unwrap the ivar array and append it to our working array
  403. unsigned int count = 0;
  404. // Will be unused if assertions are disabled.
  405. __unused int scanResult = sscanf(ivarsObj.objCType, "[%u^{objc_ivar}]", &count);
  406. ASDisplayNodeAssert(scanResult == 1, @"Unexpected type in NSValue: %s", ivarsObj.objCType);
  407. ASDisplayNodeCAssert(resultCount + count < kMaxDealloc2MainIvarsPerClassTree, @"More than %d dealloc2main ivars are not supported. Count: %d", kMaxDealloc2MainIvarsPerClassTree, resultCount + count);
  408. [ivarsObj getValue:resultIvars + resultCount];
  409. resultCount += count;
  410. }
  411. // Now gather ivars from this particular class.
  412. unsigned int allMyIvarsCount;
  413. Ivar *allMyIvars = class_copyIvarList(self, &allMyIvarsCount);
  414. for (NSUInteger i = 0; i < allMyIvarsCount; i++) {
  415. Ivar ivar = allMyIvars[i];
  416. const char *type = ivar_getTypeEncoding(ivar);
  417. if (type != NULL && strcmp(type, @encode(id)) == 0) {
  418. // If it's `id` we have to include it just in case.
  419. resultIvars[resultCount] = ivar;
  420. resultCount += 1;
  421. LOG(@"Marking ivar '%s' for possible main deallocation due to type id", ivar_getName(ivar));
  422. } else {
  423. // If it's an ivar with a static type, check the type.
  424. Class c = ASGetClassFromType(type);
  425. if (ASClassRequiresMainThreadDeallocation(c)) {
  426. resultIvars[resultCount] = ivar;
  427. resultCount += 1;
  428. LOG(@"Marking ivar '%s' for main deallocation due to class %@", ivar_getName(ivar), c);
  429. } else {
  430. LOG(@"Skipping ivar '%s' for main deallocation.", ivar_getName(ivar));
  431. }
  432. }
  433. }
  434. free(allMyIvars);
  435. // Encode the type (array of Ivars) into a string and wrap it in an NSValue
  436. char arrayType[32];
  437. snprintf(arrayType, 32, "[%u^{objc_ivar}]", resultCount);
  438. result = [NSValue valueWithBytes:resultIvars objCType:arrayType];
  439. [ivarsCache setObject:result forKey:self];
  440. return result;
  441. }
  442. #pragma mark - Loading / Unloading
  443. - (void)__unloadNode
  444. {
  445. ASDisplayNodeAssertMainThread();
  446. ASDisplayNodeAssert([self isNodeLoaded], @"Implementation shouldn't call __unloadNode if not loaded: %@", self);
  447. ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be unloaded. Node: %@", self);
  448. ASDN::MutexLocker l(__instanceLock__);
  449. if (_flags.layerBacked)
  450. _pendingViewState = [_ASPendingState pendingViewStateFromLayer:_layer];
  451. else
  452. _pendingViewState = [_ASPendingState pendingViewStateFromView:_view];
  453. [_view removeFromSuperview];
  454. _view = nil;
  455. if (_flags.layerBacked)
  456. _layer.delegate = nil;
  457. [_layer removeFromSuperlayer];
  458. _layer = nil;
  459. }
  460. - (void)__loadNode
  461. {
  462. [self layer];
  463. }
  464. - (BOOL)__shouldLoadViewOrLayer
  465. {
  466. return !(_hierarchyState & ASHierarchyStateRasterized);
  467. }
  468. - (UIView *)_viewToLoad
  469. {
  470. UIView *view;
  471. ASDN::MutexLocker l(__instanceLock__);
  472. if (_viewBlock) {
  473. view = _viewBlock();
  474. ASDisplayNodeAssertNotNil(view, @"View block returned nil");
  475. ASDisplayNodeAssert(![view isKindOfClass:[_ASDisplayView class]], @"View block should return a synchronously displayed view");
  476. _viewBlock = nil;
  477. _viewClass = [view class];
  478. } else {
  479. if (!_viewClass) {
  480. _viewClass = [self.class viewClass];
  481. }
  482. view = [[_viewClass alloc] init];
  483. }
  484. // Update flags related to special handling of UIImageView layers. More details on the flags
  485. if (_flags.synchronous && ([_viewClass isSubclassOfClass:[UIImageView class]] || [_viewClass isSubclassOfClass:[UIActivityIndicatorView class]])) {
  486. _flags.canClearContentsOfLayer = NO;
  487. _flags.canCallSetNeedsDisplayOfLayer = NO;
  488. }
  489. return view;
  490. }
  491. - (CALayer *)_layerToLoad
  492. {
  493. CALayer *layer;
  494. ASDN::MutexLocker l(__instanceLock__);
  495. ASDisplayNodeAssert(_flags.layerBacked, @"_layerToLoad is only for layer-backed nodes");
  496. if (_layerBlock) {
  497. layer = _layerBlock();
  498. ASDisplayNodeAssertNotNil(layer, @"Layer block returned nil");
  499. ASDisplayNodeAssert(![layer isKindOfClass:[_ASDisplayLayer class]], @"Layer block should return a synchronously displayed layer");
  500. _layerBlock = nil;
  501. _layerClass = [layer class];
  502. } else {
  503. if (!_layerClass) {
  504. _layerClass = [self.class layerClass];
  505. }
  506. layer = [[_layerClass alloc] init];
  507. }
  508. return layer;
  509. }
  510. - (void)_loadViewOrLayerIsLayerBacked:(BOOL)isLayerBacked
  511. {
  512. ASDN::MutexLocker l(__instanceLock__);
  513. if (_flags.isDeallocating) {
  514. return;
  515. }
  516. if (![self __shouldLoadViewOrLayer]) {
  517. return;
  518. }
  519. if (isLayerBacked) {
  520. TIME_SCOPED(_debugTimeToCreateView);
  521. _layer = [self _layerToLoad];
  522. static int ASLayerDelegateAssociationKey;
  523. /**
  524. * CALayer's .delegate property is documented to be weak, but the implementation is actually assign.
  525. * Because our layer may survive longer than the node (e.g. if someone else retains it, or if the node
  526. * begins deallocation on a background thread and it waiting for the -dealloc call to reach main), the only
  527. * way to avoid a dangling pointer is to use a weak proxy.
  528. */
  529. ASWeakProxy *instance = [ASWeakProxy weakProxyWithTarget:self];
  530. _layer.delegate = (id<CALayerDelegate>)instance;
  531. objc_setAssociatedObject(_layer, &ASLayerDelegateAssociationKey, instance, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
  532. } else {
  533. TIME_SCOPED(_debugTimeToCreateView);
  534. _view = [self _viewToLoad];
  535. _view.asyncdisplaykit_node = self;
  536. _layer = _view.layer;
  537. }
  538. _layer.asyncdisplaykit_node = self;
  539. self.asyncLayer.asyncDelegate = self;
  540. {
  541. TIME_SCOPED(_debugTimeToApplyPendingState);
  542. [self _applyPendingStateToViewOrLayer];
  543. }
  544. {
  545. TIME_SCOPED(_debugTimeToAddSubnodeViews);
  546. [self _addSubnodeViewsAndLayers];
  547. }
  548. {
  549. TIME_SCOPED(_debugTimeForDidLoad);
  550. [self __didLoad];
  551. }
  552. }
  553. - (void)__didLoad
  554. {
  555. ASDN::MutexLocker l(__instanceLock__);
  556. ASDisplayNodeLogEvent(self, @"didLoad");
  557. [self didLoad];
  558. for (ASDisplayNodeDidLoadBlock block in _onDidLoadBlocks) {
  559. block(self);
  560. }
  561. _onDidLoadBlocks = nil;
  562. }
  563. - (void)didLoad
  564. {
  565. ASDisplayNodeAssertMainThread();
  566. // Subclass hook
  567. }
  568. - (BOOL)isNodeLoaded
  569. {
  570. if (ASDisplayNodeThreadIsMain()) {
  571. // Because the view and layer can only be created and destroyed on Main, that is also the only thread
  572. // where the state of this property can change. As an optimization, we can avoid locking.
  573. return [self _isNodeLoaded];
  574. } else {
  575. ASDN::MutexLocker l(__instanceLock__);
  576. return [self _isNodeLoaded];
  577. }
  578. }
  579. - (BOOL)_isNodeLoaded
  580. {
  581. return (_view != nil || (_layer != nil && _flags.layerBacked));
  582. }
  583. #pragma mark - Misc Setter / Getter
  584. - (UIView *)view
  585. {
  586. ASDisplayNodeAssert(!_flags.layerBacked, @"Call to -view undefined on layer-backed nodes");
  587. if (_flags.layerBacked) {
  588. return nil;
  589. }
  590. if (!_view) {
  591. ASDisplayNodeAssertMainThread();
  592. [self _loadViewOrLayerIsLayerBacked:NO];
  593. }
  594. return _view;
  595. }
  596. - (CALayer *)layer
  597. {
  598. if (!_layer) {
  599. ASDisplayNodeAssertMainThread();
  600. if (!_flags.layerBacked) {
  601. return self.view.layer;
  602. }
  603. [self _loadViewOrLayerIsLayerBacked:YES];
  604. }
  605. return _layer;
  606. }
  607. // Returns nil if the layer is not an _ASDisplayLayer; will not create the layer if nil.
  608. - (_ASDisplayLayer *)asyncLayer
  609. {
  610. ASDN::MutexLocker l(__instanceLock__);
  611. return [_layer isKindOfClass:[_ASDisplayLayer class]] ? (_ASDisplayLayer *)_layer : nil;
  612. }
  613. - (BOOL)isSynchronous
  614. {
  615. ASDN::MutexLocker l(__instanceLock__);
  616. return _flags.synchronous;
  617. }
  618. - (void)setSynchronous:(BOOL)flag
  619. {
  620. ASDN::MutexLocker l(__instanceLock__);
  621. _flags.synchronous = flag;
  622. }
  623. - (void)setLayerBacked:(BOOL)isLayerBacked
  624. {
  625. if (![self.class layerBackedNodesEnabled]) return;
  626. ASDN::MutexLocker l(__instanceLock__);
  627. ASDisplayNodeAssert(!_view && !_layer, @"Cannot change isLayerBacked after layer or view has loaded");
  628. ASDisplayNodeAssert(!_viewBlock && !_layerBlock, @"Cannot change isLayerBacked when a layer or view block is provided");
  629. ASDisplayNodeAssert(!_viewClass && !_layerClass, @"Cannot change isLayerBacked when a layer or view class is provided");
  630. if (isLayerBacked != _flags.layerBacked && !_view && !_layer) {
  631. _flags.layerBacked = isLayerBacked;
  632. }
  633. }
  634. - (BOOL)isLayerBacked
  635. {
  636. ASDN::MutexLocker l(__instanceLock__);
  637. return _flags.layerBacked;
  638. }
  639. - (BOOL)shouldAnimateSizeChanges
  640. {
  641. ASDN::MutexLocker l(__instanceLock__);
  642. return _flags.shouldAnimateSizeChanges;
  643. }
  644. - (void)setShouldAnimateSizeChanges:(BOOL)shouldAnimateSizeChanges
  645. {
  646. ASDN::MutexLocker l(__instanceLock__);
  647. _flags.shouldAnimateSizeChanges = shouldAnimateSizeChanges;
  648. }
  649. - (CGRect)threadSafeBounds
  650. {
  651. ASDN::MutexLocker l(__instanceLock__);
  652. return _threadSafeBounds;
  653. }
  654. - (void)setThreadSafeBounds:(CGRect)newBounds
  655. {
  656. ASDN::MutexLocker l(__instanceLock__);
  657. _threadSafeBounds = newBounds;
  658. }
  659. #pragma mark - Layout
  660. #if DEBUG
  661. #define AS_DEDUPE_LAYOUT_SPEC_TREE 1
  662. #endif
  663. // At most a layoutSpecBlock or one of the three layout methods is overridden
  664. #define __ASDisplayNodeCheckForLayoutMethodOverrides \
  665. ASDisplayNodeAssert(_layoutSpecBlock != NULL || \
  666. ((ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \
  667. + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(layoutSpecThatFits:)) ? 1 : 0) \
  668. + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0)) <= 1, \
  669. @"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits:, layoutSpecThatFits:, or calculateSizeThatFits:", NSStringFromClass(self.class))
  670. #pragma mark <ASLayoutElement>
  671. - (ASLayoutElementStyle *)style
  672. {
  673. ASDN::MutexLocker l(__instanceLock__);
  674. if (_style == nil) {
  675. _style = [[ASLayoutElementStyle alloc] init];
  676. }
  677. return _style;
  678. }
  679. - (ASLayoutElementType)layoutElementType
  680. {
  681. return ASLayoutElementTypeDisplayNode;
  682. }
  683. - (BOOL)canLayoutAsynchronous
  684. {
  685. return !self.isNodeLoaded;
  686. }
  687. - (NSArray<id<ASLayoutElement>> *)sublayoutElements
  688. {
  689. return self.subnodes;
  690. }
  691. - (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock
  692. {
  693. styleBlock(self.style);
  694. return self;
  695. }
  696. ASLayoutElementFinalLayoutElementDefault
  697. - (NSString *)debugName
  698. {
  699. ASDN::MutexLocker l(__instanceLock__);
  700. return _debugName;
  701. }
  702. - (void)setDebugName:(NSString *)debugName
  703. {
  704. ASDN::MutexLocker l(__instanceLock__);
  705. if (!ASObjectIsEqual(_debugName, debugName)) {
  706. _debugName = [debugName copy];
  707. }
  708. }
  709. #pragma mark Measurement Pass
  710. - (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
  711. {
  712. #pragma clang diagnostic push
  713. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  714. // For now we just call the deprecated measureWithSizeRange: method to not break old API
  715. return [self measureWithSizeRange:constrainedSize];
  716. #pragma clang diagnostic pop
  717. }
  718. - (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
  719. {
  720. ASDN::MutexLocker l(__instanceLock__);
  721. // If one or multiple layout transitions are in flight it still can happen that layout information is requested
  722. // on other threads. As the pending and calculated layout to be updated in the layout transition in here just a
  723. // layout calculation wil be performed without side effect
  724. if ([self _isLayoutTransitionInvalid]) {
  725. return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize];
  726. }
  727. if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) {
  728. ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _calculatedDisplayNodeLayout->layout should not be nil! %@", self);
  729. // Our calculated layout is suitable for this constrainedSize, so keep using it and
  730. // invalidate any pending layout that has been generated in the past.
  731. _pendingDisplayNodeLayout = nullptr;
  732. return _calculatedDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}];
  733. }
  734. // Creat a pending display node layout for the layout pass
  735. _pendingDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>(
  736. [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize],
  737. constrainedSize,
  738. parentSize
  739. );
  740. ASDisplayNodeAssertNotNil(_pendingDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _pendingDisplayNodeLayout->layout should not be nil! %@", self);
  741. return _pendingDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}];
  742. }
  743. #pragma mark Layout Pass
  744. - (void)__setNeedsLayout
  745. {
  746. ASDN::MutexLocker l(__instanceLock__);
  747. [self invalidateCalculatedLayout];
  748. }
  749. - (void)invalidateCalculatedLayout
  750. {
  751. ASDN::MutexLocker l(__instanceLock__);
  752. // This will cause the next layout pass to compute a new layout instead of returning
  753. // the cached layout in case the constrained or parent size did not change
  754. _calculatedDisplayNodeLayout->invalidate();
  755. if (_pendingDisplayNodeLayout != nullptr) {
  756. _pendingDisplayNodeLayout->invalidate();
  757. }
  758. }
  759. - (void)__layout
  760. {
  761. ASDisplayNodeAssertMainThread();
  762. ASDN::MutexLocker l(__instanceLock__);
  763. CGRect bounds = _threadSafeBounds;
  764. if (CGRectEqualToRect(bounds, CGRectZero)) {
  765. // Performing layout on a zero-bounds view often results in frame calculations
  766. // with negative sizes after applying margins, which will cause
  767. // measureWithSizeRange: on subnodes to assert.
  768. LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
  769. return;
  770. }
  771. // If a current layout transition is in progress there is no need to do a measurement and layout pass in here as
  772. // this is supposed to happen within the layout transition process
  773. if ([self _isTransitionInProgress]) {
  774. return;
  775. }
  776. // This method will confirm that the layout is up to date (and update if needed).
  777. // Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning).
  778. [self _locked_measureNodeWithBoundsIfNecessary:bounds];
  779. _pendingDisplayNodeLayout = nullptr;
  780. [self _locked_layoutPlaceholderIfNecessary];
  781. [self layout];
  782. [self layoutDidFinish];
  783. }
  784. /// Needs to be called with lock held
  785. - (void)_locked_measureNodeWithBoundsIfNecessary:(CGRect)bounds
  786. {
  787. // Check if we are a subnode in a layout transition.
  788. // In this case no measurement is needed as it's part of the layout transition
  789. if ([self _isLayoutTransitionInvalid]) {
  790. return;
  791. }
  792. CGSize boundsSizeForLayout = ASCeilSizeValues(bounds.size);
  793. // Prefer _pendingDisplayNodeLayout over _calculatedDisplayNodeLayout (if exists, it's the newest)
  794. // If there is no _pending, check if _calculated is valid to reuse (avoiding recalculation below).
  795. if (_pendingDisplayNodeLayout == nullptr) {
  796. if (_calculatedDisplayNodeLayout->isDirty() == NO
  797. && (_calculatedDisplayNodeLayout->requestedLayoutFromAbove == YES
  798. || CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout))) {
  799. return;
  800. }
  801. }
  802. // _calculatedDisplayNodeLayout is not reusable we need to transition to a new one
  803. [self cancelLayoutTransition];
  804. BOOL didCreateNewContext = NO;
  805. ASLayoutElementContext context = ASLayoutElementGetCurrentContext();
  806. if (ASLayoutElementContextIsNull(context)) {
  807. context = ASLayoutElementContextMake(ASLayoutElementContextDefaultTransitionID);
  808. ASLayoutElementSetCurrentContext(context);
  809. didCreateNewContext = YES;
  810. }
  811. // Figure out previous and pending layouts for layout transition
  812. std::shared_ptr<ASDisplayNodeLayout> nextLayout = _pendingDisplayNodeLayout;
  813. #define layoutSizeDifferentFromBounds !CGSizeEqualToSize(nextLayout->layout.size, boundsSizeForLayout)
  814. // nextLayout was likely created by a call to layoutThatFits:, check if is valid and can be applied.
  815. // If our bounds size is different than it, or invalid, recalculate. Use #define to avoid nullptr->
  816. if (nextLayout == nullptr || nextLayout->isDirty() == YES || layoutSizeDifferentFromBounds) {
  817. // Use the last known constrainedSize passed from a parent during layout (if never, use bounds).
  818. ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass];
  819. ASLayout *layout = [self calculateLayoutThatFits:constrainedSize
  820. restrictedToSize:self.style.size
  821. relativeToParentSize:boundsSizeForLayout];
  822. nextLayout = std::make_shared<ASDisplayNodeLayout>(layout, constrainedSize, boundsSizeForLayout);
  823. }
  824. if (didCreateNewContext) {
  825. ASLayoutElementClearCurrentContext();
  826. }
  827. // If our new layout's desired size for self doesn't match current size, ask our parent to update it.
  828. // This can occur for either pre-calculated or newly-calculated layouts.
  829. if (nextLayout->requestedLayoutFromAbove == NO
  830. && CGSizeEqualToSize(boundsSizeForLayout, nextLayout->layout.size) == NO) {
  831. // The layout that we have specifies that this node (self) would like to be a different size
  832. // than it currently is. Because that size has been computed within the constrainedSize, we
  833. // expect that calling setNeedsLayoutFromAbove will result in our parent resizing us to this.
  834. // However, in some cases apps may manually interfere with this (setting a different bounds).
  835. // In this case, we need to detect that we've already asked to be resized to match this
  836. // particular ASLayout object, and shouldn't loop asking again unless we have a different ASLayout.
  837. nextLayout->requestedLayoutFromAbove = YES;
  838. [self setNeedsLayoutFromAbove];
  839. }
  840. // Prepare to transition to nextLayout
  841. ASDisplayNodeAssertNotNil(nextLayout->layout, @"nextLayout->layout should not be nil! %@", self);
  842. _pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
  843. pendingLayout:nextLayout
  844. previousLayout:_calculatedDisplayNodeLayout];
  845. // If a parent is currently executing a layout transition, perform our layout application after it.
  846. if (ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO) {
  847. // If no transition, apply our new layout immediately (common case).
  848. [self _completePendingLayoutTransition];
  849. }
  850. }
  851. - (ASSizeRange)_locked_constrainedSizeForLayoutPass
  852. {
  853. // TODO: The logic in -setNeedsLayoutFromAbove seems correct and doesn't use this method.
  854. // logic seems correct. For what case does -this method need to do the CGSizeEqual checks?
  855. // IF WE CAN REMOVE BOUNDS CHECKS HERE, THEN WE CAN ALSO REMOVE "REQUESTED FROM ABOVE" CHECK
  856. CGSize boundsSizeForLayout = ASCeilSizeValues(self.threadSafeBounds.size);
  857. // Checkout if constrained size of pending or calculated display node layout can be used
  858. if (_pendingDisplayNodeLayout != nullptr
  859. && (_pendingDisplayNodeLayout->requestedLayoutFromAbove
  860. || CGSizeEqualToSize(_pendingDisplayNodeLayout->layout.size, boundsSizeForLayout))) {
  861. // We assume the size from the last returned layoutThatFits: layout was applied so use the pending display node
  862. // layout constrained size
  863. return _pendingDisplayNodeLayout->constrainedSize;
  864. } else if (_calculatedDisplayNodeLayout->layout != nil
  865. && (_calculatedDisplayNodeLayout->requestedLayoutFromAbove
  866. || CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, boundsSizeForLayout))) {
  867. // We assume the _calculatedDisplayNodeLayout is still valid and the frame is not different
  868. return _calculatedDisplayNodeLayout->constrainedSize;
  869. } else {
  870. // In this case neither the _pendingDisplayNodeLayout or the _calculatedDisplayNodeLayout constrained size can
  871. // be reused, so the current bounds is used. This is usual the case if a frame was set manually that differs to
  872. // the one returned from layoutThatFits: or layoutThatFits: was never called
  873. return ASSizeRangeMake(boundsSizeForLayout);
  874. }
  875. }
  876. - (void)layoutDidFinish
  877. {
  878. // Hook for subclasses
  879. }
  880. #pragma mark Calculation
  881. - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
  882. restrictedToSize:(ASLayoutElementSize)size
  883. relativeToParentSize:(CGSize)parentSize
  884. {
  885. ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize);
  886. const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, styleAndParentSize);
  887. return [self calculateLayoutThatFits:resolvedRange];
  888. }
  889. - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
  890. {
  891. __ASDisplayNodeCheckForLayoutMethodOverrides;
  892. ASDN::MutexLocker l(__instanceLock__);
  893. #if YOGA /* YOGA */
  894. if (ASHierarchyStateIncludesYogaLayoutEnabled(_hierarchyState) == YES &&
  895. ASHierarchyStateIncludesYogaLayoutMeasuring(_hierarchyState) == NO) {
  896. ASDN::MutexUnlocker ul(__instanceLock__);
  897. return [self calculateLayoutFromYogaRoot:constrainedSize];
  898. }
  899. #endif /* YOGA */
  900. // Manual size calculation via calculateSizeThatFits:
  901. if (((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) ||
  902. (_layoutSpecBlock != NULL)) == NO) {
  903. CGSize size = [self calculateSizeThatFits:constrainedSize.max];
  904. ASDisplayNodeLogEvent(self, @"calculatedSize: %@", NSStringFromCGSize(size));
  905. return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil];
  906. }
  907. // Size calcualtion with layout elements
  908. BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec;
  909. if (measureLayoutSpec) {
  910. _layoutSpecNumberOfPasses++;
  911. }
  912. // Get layout element from the node
  913. id<ASLayoutElement> layoutElement = [self _layoutElementThatFits:constrainedSize];
  914. // Certain properties are necessary to set on an element of type ASLayoutSpec
  915. if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) {
  916. ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement;
  917. #if AS_DEDUPE_LAYOUT_SPEC_TREE
  918. NSSet *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree];
  919. if (duplicateElements.count > 0) {
  920. ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements);
  921. // Use an empty layout spec to avoid crashes
  922. layoutSpec = [[ASLayoutSpec alloc] init];
  923. }
  924. #endif
  925. ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec);
  926. layoutSpec.isMutable = NO;
  927. }
  928. // Manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection
  929. {
  930. ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
  931. ASTraitCollectionPropagateDown(layoutElement, self.primitiveTraitCollection);
  932. }
  933. BOOL measureLayoutComputation = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation;
  934. if (measureLayoutComputation) {
  935. _layoutComputationNumberOfPasses++;
  936. }
  937. // Layout element layout creation
  938. ASLayout *layout = ({
  939. ASDN::SumScopeTimer t(_layoutComputationTotalTime, measureLayoutComputation);
  940. [layoutElement layoutThatFits:constrainedSize];
  941. });
  942. ASDisplayNodeAssertNotNil(layout, @"[ASLayoutElement layoutThatFits:] should never return nil! %@, %@", self, layout);
  943. // Make sure layoutElementObject of the root layout is `self`, so that the flattened layout will be structurally correct.
  944. BOOL isFinalLayoutElement = (layout.layoutElement != self);
  945. if (isFinalLayoutElement) {
  946. layout.position = CGPointZero;
  947. layout = [ASLayout layoutWithLayoutElement:self size:layout.size sublayouts:@[layout]];
  948. }
  949. ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout);
  950. return [layout filteredNodeLayoutTree];
  951. }
  952. - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
  953. {
  954. __ASDisplayNodeCheckForLayoutMethodOverrides;
  955. #if ASDISPLAYNODE_ASSERTIONS_ENABLED
  956. if (ASIsCGSizeValidForSize(constrainedSize) == NO) {
  957. NSLog(@"Cannot calculate size of node: constrainedSize is infinite and node does not override -calculateSizeThatFits: or specify a preferredSize. Try setting style.preferredSize. Node: %@", [self displayNodeRecursiveDescription]);
  958. }
  959. #endif
  960. return ASIsCGSizeValidForSize(constrainedSize) ? constrainedSize : CGSizeZero;
  961. }
  962. - (id<ASLayoutElement>)_layoutElementThatFits:(ASSizeRange)constrainedSize
  963. {
  964. __ASDisplayNodeCheckForLayoutMethodOverrides;
  965. BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec;
  966. if (_layoutSpecBlock != NULL) {
  967. return ({
  968. ASDN::MutexLocker l(__instanceLock__);
  969. ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
  970. _layoutSpecBlock(self, constrainedSize);
  971. });
  972. } else {
  973. return ({
  974. ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
  975. [self layoutSpecThatFits:constrainedSize];
  976. });
  977. }
  978. }
  979. - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
  980. {
  981. __ASDisplayNodeCheckForLayoutMethodOverrides;
  982. ASDisplayNodeAssert(NO, @"-[ASDisplayNode layoutSpecThatFits:] should never return an empty value. One way this is caused is by calling -[super layoutSpecThatFits:] which is not currently supported.");
  983. return [[ASLayoutSpec alloc] init];
  984. }
  985. - (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
  986. {
  987. // For now there should never be an override of layoutSpecThatFits: / layoutElementThatFits: and a layoutSpecBlock
  988. ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported");
  989. ASDN::MutexLocker l(__instanceLock__);
  990. _layoutSpecBlock = layoutSpecBlock;
  991. }
  992. - (ASLayoutSpecBlock)layoutSpecBlock
  993. {
  994. ASDN::MutexLocker l(__instanceLock__);
  995. return _layoutSpecBlock;
  996. }
  997. - (ASLayout *)calculatedLayout
  998. {
  999. ASDN::MutexLocker l(__instanceLock__);
  1000. return _calculatedDisplayNodeLayout->layout;
  1001. }
  1002. - (void)setCalculatedDisplayNodeLayout:(std::shared_ptr<ASDisplayNodeLayout>)displayNodeLayout
  1003. {
  1004. ASDN::MutexLocker l(__instanceLock__);
  1005. ASDisplayNodeAssertTrue(displayNodeLayout->layout.layoutElement == self);
  1006. ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.width >= 0.0);
  1007. ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.height >= 0.0);
  1008. _calculatedDisplayNodeLayout = displayNodeLayout;
  1009. }
  1010. - (CGSize)calculatedSize
  1011. {
  1012. ASDN::MutexLocker l(__instanceLock__);
  1013. if (_pendingDisplayNodeLayout != nullptr) {
  1014. return _pendingDisplayNodeLayout->layout.size;
  1015. }
  1016. return _calculatedDisplayNodeLayout->layout.size;
  1017. }
  1018. - (ASSizeRange)constrainedSizeForCalculatedLayout
  1019. {
  1020. ASDN::MutexLocker l(__instanceLock__);
  1021. if (_pendingDisplayNodeLayout != nullptr) {
  1022. return _pendingDisplayNodeLayout->constrainedSize;
  1023. }
  1024. return _calculatedDisplayNodeLayout->constrainedSize;
  1025. }
  1026. - (void)setNeedsLayoutFromAbove
  1027. {
  1028. ASDisplayNodeAssertThreadAffinity(self);
  1029. __instanceLock__.lock();
  1030. // Mark the node for layout in the next layout pass
  1031. [self setNeedsLayout];
  1032. // Escalate to the root; entire tree must allow adjustments so the layout fits the new child.
  1033. // Much of the layout will be re-used as cached (e.g. other items in an unconstrained stack)
  1034. ASDisplayNode *supernode = _supernode;
  1035. if (supernode) {
  1036. // Threading model requires that we unlock before calling a method on our parent.
  1037. __instanceLock__.unlock();
  1038. [supernode setNeedsLayoutFromAbove];
  1039. return;
  1040. }
  1041. // We are the root node and need to re-flow the layout; at least one child needs a new size.
  1042. CGSize boundsSizeForLayout = ASCeilSizeValues(self.bounds.size);
  1043. // Figure out constrainedSize to use
  1044. ASSizeRange constrainedSize = ASSizeRangeMake(boundsSizeForLayout);
  1045. if (_pendingDisplayNodeLayout != nullptr) {
  1046. constrainedSize = _pendingDisplayNodeLayout->constrainedSize;
  1047. } else if (_calculatedDisplayNodeLayout->layout != nil) {
  1048. constrainedSize = _calculatedDisplayNodeLayout->constrainedSize;
  1049. }
  1050. // Perform a measurement pass to get the full tree layout, adapting to the child's new size.
  1051. ASLayout *layout = [self layoutThatFits:constrainedSize];
  1052. // Check if the returned layout has a different size than our current bounds.
  1053. if (CGSizeEqualToSize(boundsSizeForLayout, layout.size) == NO) {
  1054. // If so, inform our container we need an update (e.g Table, Collection, ViewController, etc).
  1055. [self _locked_displayNodeDidInvalidateSizeNewSize:layout.size];
  1056. }
  1057. __instanceLock__.unlock();
  1058. }
  1059. - (void)_locked_displayNodeDidInvalidateSizeNewSize:(CGSize)size
  1060. {
  1061. ASDisplayNodeAssertThreadAffinity(self);
  1062. // The default implementation of display node changes the size of itself to the new size
  1063. CGRect oldBounds = self.bounds;
  1064. CGSize oldSize = oldBounds.size;
  1065. CGSize newSize = size;
  1066. if (! CGSizeEqualToSize(oldSize, newSize)) {
  1067. self.bounds = (CGRect){ oldBounds.origin, newSize };
  1068. // Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint
  1069. // and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted.
  1070. CGPoint anchorPoint = self.anchorPoint;
  1071. CGPoint oldPosition = self.position;
  1072. CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x;
  1073. CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y;
  1074. self.position = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta);
  1075. }
  1076. }
  1077. - (void)layout
  1078. {
  1079. ASDisplayNodeAssertMainThread();
  1080. __instanceLock__.lock();
  1081. if (_calculatedDisplayNodeLayout->isDirty()) {
  1082. __instanceLock__.unlock();
  1083. return;
  1084. }
  1085. [self _locked_layoutSublayouts];
  1086. __instanceLock__.unlock();
  1087. }
  1088. - (void)_locked_layoutSublayouts
  1089. {
  1090. for (ASLayout *subnodeLayout in _calculatedDisplayNodeLayout->layout.sublayouts) {
  1091. ((ASDisplayNode *)subnodeLayout.layoutElement).frame = subnodeLayout.frame;
  1092. }
  1093. }
  1094. #pragma mark Automatically Manages Subnodes
  1095. - (BOOL)automaticallyManagesSubnodes
  1096. {
  1097. ASDN::MutexLocker l(__instanceLock__);
  1098. return _automaticallyManagesSubnodes;
  1099. }
  1100. - (void)setAutomaticallyManagesSubnodes:(BOOL)automaticallyManagesSubnodes
  1101. {
  1102. ASDN::MutexLocker l(__instanceLock__);
  1103. _automaticallyManagesSubnodes = automaticallyManagesSubnodes;
  1104. }
  1105. #pragma mark Layout Transition
  1106. - (void)transitionLayoutWithAnimation:(BOOL)animated
  1107. shouldMeasureAsync:(BOOL)shouldMeasureAsync
  1108. measurementCompletion:(void(^)())completion
  1109. {
  1110. ASDisplayNodeAssertMainThread();
  1111. [self setNeedsLayout];
  1112. [self transitionLayoutWithSizeRange:[self _locked_constrainedSizeForLayoutPass]
  1113. animated:animated
  1114. shouldMeasureAsync:shouldMeasureAsync
  1115. measurementCompletion:completion];
  1116. }
  1117. - (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize
  1118. animated:(BOOL)animated
  1119. shouldMeasureAsync:(BOOL)shouldMeasureAsync
  1120. measurementCompletion:(void(^)())completion
  1121. {
  1122. ASDisplayNodeAssertMainThread();
  1123. if (constrainedSize.max.width <= 0.0 || constrainedSize.max.height <= 0.0) {
  1124. // Using CGSizeZero for the sizeRange can cause negative values in client layout code.
  1125. // Most likely called transitionLayout: without providing a size, before first layout pass.
  1126. return;
  1127. }
  1128. // Check if we are a subnode in a layout transition.
  1129. // In this case no measurement is needed as we're part of the layout transition.
  1130. if ([self _isLayoutTransitionInvalid]) {
  1131. return;
  1132. }
  1133. {
  1134. ASDN::MutexLocker l(__instanceLock__);
  1135. ASDisplayNodeAssert(ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO, @"Can't start a transition when one of the supernodes is performing one.");
  1136. }
  1137. // Every new layout transition has a transition id associated to check in subsequent transitions for cancelling
  1138. int32_t transitionID = [self _startNewTransition];
  1139. // Move all subnodes in layout pending state for this transition
  1140. ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
  1141. ASDisplayNodeAssert([node _isTransitionInProgress] == NO, @"Can't start a transition when one of the subnodes is performing one.");
  1142. node.hierarchyState |= ASHierarchyStateLayoutPending;
  1143. node.pendingTransitionID = transitionID;
  1144. });
  1145. // Transition block that executes the layout transition
  1146. void (^transitionBlock)(void) = ^{
  1147. if ([self _shouldAbortTransitionWithID:transitionID]) {
  1148. return;
  1149. }
  1150. // Perform a full layout creation pass with passed in constrained size to create the new layout for the transition
  1151. ASLayout *newLayout;
  1152. {
  1153. ASDN::MutexLocker l(__instanceLock__);
  1154. ASLayoutElementSetCurrentContext(ASLayoutElementContextMake(transitionID));
  1155. BOOL automaticallyManagesSubnodesDisabled = (self.automaticallyManagesSubnodes == NO);
  1156. self.automaticallyManagesSubnodes = YES; // Temporary flag for 1.9.x
  1157. newLayout = [self calculateLayoutThatFits:constrainedSize
  1158. restrictedToSize:self.style.size
  1159. relativeToParentSize:constrainedSize.max];
  1160. if (automaticallyManagesSubnodesDisabled) {
  1161. self.automaticallyManagesSubnodes = NO; // Temporary flag for 1.9.x
  1162. }
  1163. ASLayoutElementClearCurrentContext();
  1164. }
  1165. if ([self _shouldAbortTransitionWithID:transitionID]) {
  1166. return;
  1167. }
  1168. ASPerformBlockOnMainThread(^{
  1169. // Grab __instanceLock__ here to make sure this transition isn't invalidated
  1170. // right after it passed the validation test and before it proceeds
  1171. ASDN::MutexLocker l(__instanceLock__);
  1172. if ([self _shouldAbortTransitionWithID:transitionID]) {
  1173. return;
  1174. }
  1175. // Update calculated layout
  1176. auto previousLayout = _calculatedDisplayNodeLayout;
  1177. auto pendingLayout = std::make_shared<ASDisplayNodeLayout>(
  1178. newLayout,
  1179. constrainedSize,
  1180. constrainedSize.max
  1181. );
  1182. [self setCalculatedDisplayNodeLayout:pendingLayout];
  1183. // Apply complete layout transitions for all subnodes
  1184. ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
  1185. [node _completePendingLayoutTransition];
  1186. node.hierarchyState &= (~ASHierarchyStateLayoutPending);
  1187. });
  1188. // Measurement pass completion
  1189. if (completion) {
  1190. completion();
  1191. }
  1192. // Setup pending layout transition for animation
  1193. _pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
  1194. pendingLayout:pendingLayout
  1195. previousLayout:previousLayout];
  1196. // Setup context for pending layout transition. we need to hold a strong reference to the context
  1197. _pendingLayoutTransitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated
  1198. layoutDelegate:_pendingLayoutTransition
  1199. completionDelegate:self];
  1200. // Apply the subnode insertion immediately to be able to animate the nodes
  1201. [_pendingLayoutTransition applySubnodeInsertions];
  1202. // Kick off animating the layout transition
  1203. [self animateLayoutTransition:_pendingLayoutTransitionContext];
  1204. // Mark transaction as finished
  1205. [self _finishOrCancelTransition];
  1206. });
  1207. };
  1208. // Start transition based on flag on current or background thread
  1209. if (shouldMeasureAsync) {
  1210. ASPerformBlockOnBackgroundThread(transitionBlock);
  1211. } else {
  1212. transitionBlock();
  1213. }
  1214. }
  1215. - (void)cancelLayoutTransition
  1216. {
  1217. ASDN::MutexLocker l(__instanceLock__);
  1218. if ([self _isTransitionInProgress]) {
  1219. // Cancel transition in progress
  1220. [self _finishOrCancelTransition];
  1221. // Tell subnodes to exit layout pending state and clear related properties
  1222. ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
  1223. node.hierarchyState &= (~ASHierarchyStateLayoutPending);
  1224. });
  1225. }
  1226. }
  1227. - (BOOL)_isTransitionInProgress
  1228. {
  1229. ASDN::MutexLocker l(__instanceLock__);
  1230. return _transitionInProgress;
  1231. }
  1232. - (BOOL)_isLayoutTransitionInvalid
  1233. {
  1234. ASDN::MutexLocker l(__instanceLock__);
  1235. if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) {
  1236. ASLayoutElementContext context = ASLayoutElementGetCurrentContext();
  1237. if (ASLayoutElementContextIsNull(context) || _pendingTransitionID != context.transitionID) {
  1238. return YES;
  1239. }
  1240. }
  1241. return NO;
  1242. }
  1243. /// Starts a new transition and returns the transition id
  1244. - (int32_t)_startNewTransition
  1245. {
  1246. ASDN::MutexLocker l(__instanceLock__);
  1247. _transitionInProgress = YES;
  1248. _transitionID = OSAtomicAdd32(1, &_transitionID);
  1249. return _transitionID;
  1250. }
  1251. - (void)_finishOrCancelTransition
  1252. {
  1253. ASDN::MutexLocker l(__instanceLock__);
  1254. _transitionInProgress = NO;
  1255. }
  1256. - (void)setPendingTransitionID:(int32_t)pendingTransitionID
  1257. {
  1258. ASDN::MutexLocker l(__instanceLock__);
  1259. ASDisplayNodeAssertTrue(_pendingTransitionID < pendingTransitionID);
  1260. _pendingTransitionID = pendingTransitionID;
  1261. }
  1262. - (int32_t)pendingTransitionID
  1263. {
  1264. ASDN::MutexLocker l(__instanceLock__);
  1265. return _pendingTransitionID;
  1266. }
  1267. - (BOOL)_shouldAbortTransitionWithID:(int32_t)transitionID
  1268. {
  1269. ASDN::MutexLocker l(__instanceLock__);
  1270. return (!_transitionInProgress || _transitionID != transitionID);
  1271. }
  1272. - (void)setDefaultLayoutTransitionDuration:(NSTimeInterval)defaultLayoutTransitionDuration
  1273. {
  1274. ASDN::MutexLocker l(__instanceLock__);
  1275. _defaultLayoutTransitionDuration = defaultLayoutTransitionDuration;
  1276. }
  1277. - (NSTimeInterval)defaultLayoutTransitionDuration
  1278. {
  1279. ASDN::MutexLocker l(__instanceLock__);
  1280. return _defaultLayoutTransitionDuration;
  1281. }
  1282. - (void)setDefaultLayoutTransitionDelay:(NSTimeInterval)defaultLayoutTransitionDelay
  1283. {
  1284. ASDN::MutexLocker l(__instanceLock__);
  1285. _defaultLayoutTransitionDelay = defaultLayoutTransitionDelay;
  1286. }
  1287. - (NSTimeInterval)defaultLayoutTransitionDelay
  1288. {
  1289. ASDN::MutexLocker l(__instanceLock__);
  1290. return _defaultLayoutTransitionDelay;
  1291. }
  1292. - (void)setDefaultLayoutTransitionOptions:(UIViewAnimationOptions)defaultLayoutTransitionOptions
  1293. {
  1294. ASDN::MutexLocker l(__instanceLock__);
  1295. _defaultLayoutTransitionOptions = defaultLayoutTransitionOptions;
  1296. }
  1297. - (UIViewAnimationOptions)defaultLayoutTransitionOptions
  1298. {
  1299. ASDN::MutexLocker l(__instanceLock__);
  1300. return _defaultLayoutTransitionOptions;
  1301. }
  1302. #pragma mark <LayoutTransitioning>
  1303. /*
  1304. * Hook for subclasses to perform an animation based on the given ASContextTransitioning. By default a fade in and out
  1305. * animation is provided.
  1306. */
  1307. - (void)animateLayoutTransition:(id<ASContextTransitioning>)context
  1308. {
  1309. if ([context isAnimated] == NO) {
  1310. ASDN::MutexLocker l(__instanceLock__);
  1311. [self _locked_layoutSublayouts];
  1312. [context completeTransition:YES];
  1313. return;
  1314. }
  1315. ASDisplayNode *node = self;
  1316. NSAssert(node.isNodeLoaded == YES, @"Invalid node state");
  1317. NSArray<ASDisplayNode *> *removedSubnodes = [context removedSubnodes];
  1318. NSMutableArray<ASDisplayNode *> *insertedSubnodes = [[context insertedSubnodes] mutableCopy];
  1319. NSMutableArray<ASDisplayNode *> *movedSubnodes = [NSMutableArray array];
  1320. NSMutableArray<_ASAnimatedTransitionContext *> *insertedSubnodeContexts = [NSMutableArray array];
  1321. NSMutableArray<_ASAnimatedTransitionContext *> *removedSubnodeContexts = [NSMutableArray array];
  1322. for (ASDisplayNode *subnode in [context subnodesForKey:ASTransitionContextToLayoutKey]) {
  1323. if ([insertedSubnodes containsObject:subnode] == NO) {
  1324. // This is an existing subnode, check if it is resized, moved or both
  1325. CGRect fromFrame = [context initialFrameForNode:subnode];
  1326. CGRect toFrame = [context finalFrameForNode:subnode];
  1327. if (CGSizeEqualToSize(fromFrame.size, toFrame.size) == NO) {
  1328. [insertedSubnodes addObject:subnode];
  1329. }
  1330. if (CGPointEqualToPoint(fromFrame.origin, toFrame.origin) == NO) {
  1331. [movedSubnodes addObject:subnode];
  1332. }
  1333. }
  1334. }
  1335. // Create contexts for inserted and removed subnodes
  1336. for (ASDisplayNode *insertedSubnode in insertedSubnodes) {
  1337. [insertedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:insertedSubnode alpha:insertedSubnode.alpha]];
  1338. }
  1339. for (ASDisplayNode *removedSubnode in removedSubnodes) {
  1340. [removedSubnodeContexts addObject:[_ASAnimatedTransitionContext contextForNode:removedSubnode alpha:removedSubnode.alpha]];
  1341. }
  1342. // Fade out inserted subnodes
  1343. for (ASDisplayNode *insertedSubnode in insertedSubnodes) {
  1344. insertedSubnode.frame = [context finalFrameForNode:insertedSubnode];
  1345. insertedSubnode.alpha = 0;
  1346. }
  1347. // Adjust groupOpacity for animation
  1348. BOOL originAllowsGroupOpacity = node.allowsGroupOpacity;
  1349. node.allowsGroupOpacity = YES;
  1350. [UIView animateWithDuration:self.defaultLayoutTransitionDuration delay:self.defaultLayoutTransitionDelay options:self.defaultLayoutTransitionOptions animations:^{
  1351. // Fade removed subnodes and views out
  1352. for (ASDisplayNode *removedSubnode in removedSubnodes) {
  1353. removedSubnode.alpha = 0;
  1354. }
  1355. // Fade inserted subnodes in
  1356. for (_ASAnimatedTransitionContext *insertedSubnodeContext in insertedSubnodeContexts) {
  1357. insertedSubnodeContext.node.alpha = insertedSubnodeContext.alpha;
  1358. }
  1359. // Update frame of self and moved subnodes
  1360. CGSize fromSize = [context layoutForKey:ASTransitionContextFromLayoutKey].size;
  1361. CGSize toSize = [context layoutForKey:ASTransitionContextToLayoutKey].size;
  1362. BOOL isResized = (CGSizeEqualToSize(fromSize, toSize) == NO);
  1363. if (isResized == YES) {
  1364. CGPoint position = node.frame.origin;
  1365. node.frame = CGRectMake(position.x, position.y, toSize.width, toSize.height);
  1366. }
  1367. for (ASDisplayNode *movedSubnode in movedSubnodes) {
  1368. movedSubnode.frame = [context finalFrameForNode:movedSubnode];
  1369. }
  1370. } completion:^(BOOL finished) {
  1371. // Restore all removed subnode alpha values
  1372. for (_ASAnimatedTransitionContext *removedSubnodeContext in removedSubnodeContexts) {
  1373. removedSubnodeContext.node.alpha = removedSubnodeContext.alpha;
  1374. }
  1375. // Restore group opacity
  1376. node.allowsGroupOpacity = originAllowsGroupOpacity;
  1377. // Subnode removals are automatically performed
  1378. [context completeTransition:finished];
  1379. }];
  1380. }
  1381. /**
  1382. * Hook for subclasses to clean up nodes after the transition happened. Furthermore this can be used from subclasses
  1383. * to manually perform deletions.
  1384. */
  1385. - (void)didCompleteLayoutTransition:(id<ASContextTransitioning>)context
  1386. {
  1387. [_pendingLayoutTransition applySubnodeRemovals];
  1388. }
  1389. #pragma mark <_ASTransitionContextCompletionDelegate>
  1390. /**
  1391. * After completeTransition: is called on the ASContextTransitioning object in animateLayoutTransition: this
  1392. * delegate method will be called that start the completion process of the transition
  1393. */
  1394. - (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete
  1395. {
  1396. [self didCompleteLayoutTransition:context];
  1397. _pendingLayoutTransitionContext = nil;
  1398. [self _pendingLayoutTransitionDidComplete];
  1399. }
  1400. /**
  1401. * Completes the pending layout transition immediately without going through the the Layout Transition Animation API
  1402. */
  1403. - (void)_completePendingLayoutTransition
  1404. {
  1405. ASDN::MutexLocker l(__instanceLock__);
  1406. if (_pendingLayoutTransition) {
  1407. [self setCalculatedDisplayNodeLayout:_pendingLayoutTransition.pendingLayout];
  1408. [self _completeLayoutTransition:_pendingLayoutTransition];
  1409. }
  1410. [self _pendingLayoutTransitionDidComplete];
  1411. }
  1412. /**
  1413. * Can be directly called to commit the given layout transition immediately to complete without calling through to the
  1414. * Layout Transition Animation API
  1415. */
  1416. - (void)_completeLayoutTransition:(ASLayoutTransition *)layoutTransition
  1417. {
  1418. // Layout transition is not supported for nodes that are not have automatic subnode management enabled
  1419. if (layoutTransition == nil || self.automaticallyManagesSubnodes == NO) {
  1420. return;
  1421. }
  1422. // Trampoline to the main thread if necessary
  1423. if (ASDisplayNodeThreadIsMain() || layoutTransition.isSynchronous == NO) {
  1424. [layoutTransition commitTransition];
  1425. } else {
  1426. // Subnode insertions and removals need to happen always on the main thread if at least one subnode is already loaded
  1427. ASPerformBlockOnMainThread(^{
  1428. [layoutTransition commitTransition];
  1429. });
  1430. }
  1431. }
  1432. - (void)_pendingLayoutTransitionDidComplete
  1433. {
  1434. ASDN::MutexLocker l(__instanceLock__);
  1435. // Subclass hook
  1436. [self calculatedLayoutDidChange];
  1437. // We generate placeholders at measureWithSizeRange: time so that a node is guaranteed to have a placeholder ready to go.
  1438. // This is also because measurement is usually asynchronous, but placeholders need to be set up synchronously.
  1439. // First measurement is guaranteed to be before the node is onscreen, so we can create the image async. but still have it appear sync.
  1440. if (_placeholderEnabled && !_placeholderImage && [self __locked_displaysAsynchronously]) {
  1441. // Zero-sized nodes do not require a placeholder.
  1442. ASLayout *layout = _calculatedDisplayNodeLayout->layout;
  1443. CGSize layoutSize = (layout ? layout.size : CGSizeZero);
  1444. if (layoutSize.width * layoutSize.height <= 0.0) {
  1445. return;
  1446. }
  1447. // If we've displayed our contents, we don't need a placeholder.
  1448. // Contents is a thread-affined property and can't be read off main after loading.
  1449. if (self.isNodeLoaded) {
  1450. ASPerformBlockOnMainThread(^{
  1451. if (self.contents == nil) {
  1452. _placeholderImage = [self placeholderImage];
  1453. }
  1454. });
  1455. } else {
  1456. if (self.contents == nil) {
  1457. _placeholderImage = [self placeholderImage];
  1458. }
  1459. }
  1460. }
  1461. // Cleanup pending layout transition
  1462. _pendingLayoutTransition = nil;
  1463. }
  1464. - (void)calculatedLayoutDidChange
  1465. {
  1466. // subclass override
  1467. }
  1468. #pragma mark - Display
  1469. NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes";
  1470. NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp";
  1471. - (BOOL)displaysAsynchronously
  1472. {
  1473. ASDN::MutexLocker l(__instanceLock__);
  1474. return [self __locked_displaysAsynchronously];
  1475. }
  1476. /**
  1477. * Core implementation of -displaysAsynchronously.
  1478. * Must be called with __instanceLock__ held.
  1479. */
  1480. - (BOOL)__locked_displaysAsynchronously
  1481. {
  1482. return _flags.synchronous == NO && _flags.displaysAsynchronously;
  1483. }
  1484. - (void)setDisplaysAsynchronously:(BOOL)displaysAsynchronously
  1485. {
  1486. ASDisplayNodeAssertThreadAffinity(self);
  1487. // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel)
  1488. if (_flags.synchronous)
  1489. return;
  1490. ASDN::MutexLocker l(__instanceLock__);
  1491. if (_flags.displaysAsynchronously == displaysAsynchronously)
  1492. return;
  1493. _flags.displaysAsynchronously = displaysAsynchronously;
  1494. self.asyncLayer.displaysAsynchronously = displaysAsynchronously;
  1495. }
  1496. - (BOOL)shouldRasterizeDescendants
  1497. {
  1498. ASDN::MutexLocker l(__instanceLock__);
  1499. ASDisplayNodeAssert(!((_hierarchyState & ASHierarchyStateRasterized) && _flags.shouldRasterizeDescendants),
  1500. @"Subnode of a rasterized node should not have redundant shouldRasterizeDescendants enabled");
  1501. return _flags.shouldRasterizeDescendants;
  1502. }
  1503. - (void)setShouldRasterizeDescendants:(BOOL)shouldRasterize
  1504. {
  1505. ASDisplayNodeAssertThreadAffinity(self);
  1506. BOOL rasterizedFromSelfOrAncestor = NO;
  1507. {
  1508. ASDN::MutexLocker l(__instanceLock__);
  1509. if (_flags.shouldRasterizeDescendants == shouldRasterize)
  1510. return;
  1511. _flags.shouldRasterizeDescendants = shouldRasterize;
  1512. rasterizedFromSelfOrAncestor = shouldRasterize || ASHierarchyStateIncludesRasterized(_hierarchyState);
  1513. }
  1514. if (self.isNodeLoaded) {
  1515. // Recursively tear down or build up subnodes.
  1516. // TODO: When disabling rasterization, preserve rasterized backing store as placeholderImage
  1517. // while the newly materialized subtree finishes rendering. Then destroy placeholderImage to save memory.
  1518. [self recursivelyClearContents];
  1519. ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode *node) {
  1520. if (rasterizedFromSelfOrAncestor) {
  1521. [node enterHierarchyState:ASHierarchyStateRasterized];
  1522. if (node.isNodeLoaded) {
  1523. [node __unloadNode];
  1524. }
  1525. } else {
  1526. [node exitHierarchyState:ASHierarchyStateRasterized];
  1527. // We can avoid eagerly loading this node. We will load it on-demand as usual.
  1528. }
  1529. });
  1530. if (!rasterizedFromSelfOrAncestor) {
  1531. // If we are not going to rasterize at all, go ahead and set up our view hierarchy.
  1532. [self _addSubnodeViewsAndLayers];
  1533. }
  1534. if (ASInterfaceStateIncludesVisible(self.interfaceState)) {
  1535. // TODO: Change this to recursivelyEnsureDisplay - but need a variant that does not skip
  1536. // nodes that have shouldBypassEnsureDisplay set (such as image nodes) so they are rasterized.
  1537. [self recursivelyDisplayImmediately];
  1538. }
  1539. } else {
  1540. ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode *node) {
  1541. if (rasterizedFromSelfOrAncestor) {
  1542. [node enterHierarchyState:ASHierarchyStateRasterized];
  1543. } else {
  1544. [node exitHierarchyState:ASHierarchyStateRasterized];
  1545. }
  1546. });
  1547. }
  1548. }
  1549. - (CGFloat)contentsScaleForDisplay
  1550. {
  1551. ASDisplayNodeAssertThreadAffinity(self);
  1552. ASDN::MutexLocker l(__instanceLock__);
  1553. return _contentsScaleForDisplay;
  1554. }
  1555. - (void)setContentsScaleForDisplay:(CGFloat)contentsScaleForDisplay
  1556. {
  1557. ASDisplayNodeAssertThreadAffinity(self);
  1558. ASDN::MutexLocker l(__instanceLock__);
  1559. if (_contentsScaleForDisplay == contentsScaleForDisplay)
  1560. return;
  1561. _contentsScaleForDisplay = contentsScaleForDisplay;
  1562. }
  1563. - (void)displayImmediately
  1564. {
  1565. ASDisplayNodeAssertMainThread();
  1566. ASDisplayNodeAssert(!_flags.synchronous, @"this method is designed for asynchronous mode only");
  1567. [[self asyncLayer] displayImmediately];
  1568. }
  1569. - (void)recursivelyDisplayImmediately
  1570. {
  1571. ASDN::MutexLocker l(__instanceLock__);
  1572. for (ASDisplayNode *child in _subnodes) {
  1573. [child recursivelyDisplayImmediately];
  1574. }
  1575. [self displayImmediately];
  1576. }
  1577. - (void)__setNeedsDisplay
  1578. {
  1579. BOOL nowDisplay = ASInterfaceStateIncludesDisplay(_interfaceState);
  1580. // FIXME: This should not need to recursively display, so create a non-recursive variant.
  1581. // The semantics of setNeedsDisplay (as defined by CALayer behavior) are not recursive.
  1582. if (_layer && !_flags.synchronous && nowDisplay && [self __implementsDisplay]) {
  1583. [ASDisplayNode scheduleNodeForRecursiveDisplay:self];
  1584. }
  1585. }
  1586. + (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node
  1587. {
  1588. static dispatch_once_t onceToken;
  1589. static ASRunLoopQueue<ASDisplayNode *> *renderQueue;
  1590. dispatch_once(&onceToken, ^{
  1591. renderQueue = [[ASRunLoopQueue<ASDisplayNode *> alloc] initWithRunLoop:CFRunLoopGetMain()
  1592. andHandler:^(ASDisplayNode * _Nonnull dequeuedItem, BOOL isQueueDrained) {
  1593. [dequeuedItem _recursivelyTriggerDisplayAndBlock:NO];
  1594. if (isQueueDrained) {
  1595. CFTimeInterval timestamp = CACurrentMediaTime();
  1596. [[NSNotificationCenter defaultCenter] postNotificationName:ASRenderingEngineDidDisplayScheduledNodesNotification
  1597. object:nil
  1598. userInfo:@{ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp: @(timestamp)}];
  1599. }
  1600. }];
  1601. });
  1602. [renderQueue enqueue:node];
  1603. }
  1604. /// Helper method to summarize whether or not the node run through the display process
  1605. - (BOOL)__implementsDisplay
  1606. {
  1607. return _flags.implementsDrawRect || _flags.implementsImageDisplay || _flags.shouldRasterizeDescendants ||
  1608. _flags.implementsInstanceDrawRect || _flags.implementsInstanceImageDisplay;
  1609. }
  1610. // Track that a node will be displayed as part of the current node hierarchy.
  1611. // The node sending the message should usually be passed as the parameter, similar to the delegation pattern.
  1612. - (void)_pendingNodeWillDisplay:(ASDisplayNode *)node
  1613. {
  1614. ASDisplayNodeAssertMainThread();
  1615. if (!_pendingDisplayNodes) {
  1616. _pendingDisplayNodes = [[ASWeakSet alloc] init];
  1617. }
  1618. [_pendingDisplayNodes addObject:node];
  1619. }
  1620. // Notify that a node that was pending display finished
  1621. // The node sending the message should usually be passed as the parameter, similar to the delegation pattern.
  1622. - (void)_pendingNodeDidDisplay:(ASDisplayNode *)node
  1623. {
  1624. ASDisplayNodeAssertMainThread();
  1625. [_pendingDisplayNodes removeObject:node];
  1626. if (_pendingDisplayNodes.isEmpty) {
  1627. [self hierarchyDisplayDidFinish];
  1628. if (_placeholderLayer.superlayer && ![self placeholderShouldPersist]) {
  1629. void (^cleanupBlock)() = ^{
  1630. [_placeholderLayer removeFromSuperlayer];
  1631. };
  1632. if (_placeholderFadeDuration > 0.0 && ASInterfaceStateIncludesVisible(self.interfaceState)) {
  1633. [CATransaction begin];
  1634. [CATransaction setCompletionBlock:cleanupBlock];
  1635. [CATransaction setAnimationDuration:_placeholderFadeDuration];
  1636. _placeholderLayer.opacity = 0.0;
  1637. [CATransaction commit];
  1638. } else {
  1639. cleanupBlock();
  1640. }
  1641. }
  1642. }
  1643. }
  1644. - (void)hierarchyDisplayDidFinish
  1645. {
  1646. // Subclass hook
  1647. }
  1648. // Helper method to determine if it's safe to call setNeedsDisplay on a layer without throwing away the content.
  1649. // For details look at the comment on the canCallSetNeedsDisplayOfLayer flag
  1650. - (BOOL)__canCallSetNeedsDisplayOfLayer
  1651. {
  1652. ASDN::MutexLocker l(__instanceLock__);
  1653. return _flags.canCallSetNeedsDisplayOfLayer;
  1654. }
  1655. void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
  1656. {
  1657. // This recursion must handle layers in various states:
  1658. // 1. Just added to hierarchy, CA hasn't yet called -display
  1659. // 2. Previously in a hierarchy (such as a working window owned by an Intelligent Preloading class, like ASTableView / ASCollectionView / ASViewController)
  1660. // 3. Has no content to display at all
  1661. // Specifically for case 1), we need to explicitly trigger a -display call now.
  1662. // Otherwise, there is no opportunity to block the main thread after CoreAnimation's transaction commit
  1663. // (even a runloop observer at a late call order will not stop the next frame from compositing, showing placeholders).
  1664. ASDisplayNode *node = [layer asyncdisplaykit_node];
  1665. if (node.isSynchronous && [node __canCallSetNeedsDisplayOfLayer]) {
  1666. // Layers for UIKit components that are wrapped within a node needs to be set to be displayed as the contents of
  1667. // the layer get's cleared and would not be recreated otherwise.
  1668. // We do not call this for _ASDisplayLayer as an optimization.
  1669. [layer setNeedsDisplay];
  1670. }
  1671. if ([node __implementsDisplay]) {
  1672. // For layers that do get displayed here, this immediately kicks off the work on the concurrent -[_ASDisplayLayer displayQueue].
  1673. // At the same time, it creates an associated _ASAsyncTransaction, which we can use to block on display completion. See ASDisplayNode+AsyncDisplay.mm.
  1674. [layer displayIfNeeded];
  1675. }
  1676. // Kick off the recursion first, so that all necessary display calls are sent and the displayQueue is full of parallelizable work.
  1677. // NOTE: The docs report that `sublayers` returns a copy but it actually doesn't.
  1678. for (CALayer *sublayer in [layer.sublayers copy]) {
  1679. recursivelyTriggerDisplayForLayer(sublayer, shouldBlock);
  1680. }
  1681. if (shouldBlock) {
  1682. // As the recursion unwinds, verify each transaction is complete and block if it is not.
  1683. // While blocking on one transaction, others may be completing concurrently, so it doesn't matter which blocks first.
  1684. BOOL waitUntilComplete = (!node.shouldBypassEnsureDisplay);
  1685. if (waitUntilComplete) {
  1686. for (_ASAsyncTransaction *transaction in [layer.asyncdisplaykit_asyncLayerTransactions copy]) {
  1687. // Even if none of the layers have had a chance to start display earlier, they will still be allowed to saturate a multicore CPU while blocking main.
  1688. // This significantly reduces time on the main thread relative to UIKit.
  1689. [transaction waitUntilComplete];
  1690. }
  1691. }
  1692. }
  1693. }
  1694. - (void)_recursivelyTriggerDisplayAndBlock:(BOOL)shouldBlock
  1695. {
  1696. ASDisplayNodeAssertMainThread();
  1697. CALayer *layer = self.layer;
  1698. // -layoutIfNeeded is recursive, and even walks up to superlayers to check if they need layout,
  1699. // so we should call it outside of starting the recursion below. If our own layer is not marked
  1700. // as dirty, we can assume layout has run on this subtree before.
  1701. if ([layer needsLayout]) {
  1702. [layer layoutIfNeeded];
  1703. }
  1704. recursivelyTriggerDisplayForLayer(layer, shouldBlock);
  1705. }
  1706. - (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously
  1707. {
  1708. [self _recursivelyTriggerDisplayAndBlock:synchronously];
  1709. }
  1710. - (void)setShouldBypassEnsureDisplay:(BOOL)shouldBypassEnsureDisplay
  1711. {
  1712. _flags.shouldBypassEnsureDisplay = shouldBypassEnsureDisplay;
  1713. }
  1714. - (BOOL)shouldBypassEnsureDisplay
  1715. {
  1716. return _flags.shouldBypassEnsureDisplay;
  1717. }
  1718. - (void)setNeedsDisplayAtScale:(CGFloat)contentsScale
  1719. {
  1720. ASDN::MutexLocker l(__instanceLock__);
  1721. if (contentsScale != self.contentsScaleForDisplay) {
  1722. self.contentsScaleForDisplay = contentsScale;
  1723. [self setNeedsDisplay];
  1724. }
  1725. }
  1726. - (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale
  1727. {
  1728. ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) {
  1729. [node setNeedsDisplayAtScale:contentsScale];
  1730. });
  1731. }
  1732. - (void)recursivelySetDisplaySuspended:(BOOL)flag
  1733. {
  1734. _recursivelySetDisplaySuspended(self, nil, flag);
  1735. }
  1736. // TODO: Replace this with ASDisplayNodePerformBlockOnEveryNode or a variant with a condition / test block.
  1737. static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, BOOL flag)
  1738. {
  1739. // If there is no layer, but node whose its view is loaded, then we can traverse down its layer hierarchy. Otherwise we must stick to the node hierarchy to avoid loading views prematurely. Note that for nodes that haven't loaded their views, they can't possibly have subviews/sublayers, so we don't need to traverse the layer hierarchy for them.
  1740. if (!layer && node && node.nodeLoaded) {
  1741. layer = node.layer;
  1742. }
  1743. // If we don't know the node, but the layer is an async layer, get the node from the layer.
  1744. if (!node && layer && [layer isKindOfClass:[_ASDisplayLayer class]]) {
  1745. node = layer.asyncdisplaykit_node;
  1746. }
  1747. // Set the flag on the node. If this is a pure layer (no node) then this has no effect (plain layers don't support preventing/cancelling display).
  1748. node.displaySuspended = flag;
  1749. if (layer && !node.shouldRasterizeDescendants) {
  1750. // If there is a layer, recurse down the layer hierarchy to set the flag on descendants. This will cover both layer-based and node-based children.
  1751. for (CALayer *sublayer in layer.sublayers) {
  1752. _recursivelySetDisplaySuspended(nil, sublayer, flag);
  1753. }
  1754. } else {
  1755. // If there is no layer (view not loaded yet) or this node rasterizes descendants (there won't be a layer tree to traverse), recurse down the subnode hierarchy to set the flag on descendants. This covers only node-based children, but for a node whose view is not loaded it can't possibly have nodeless children.
  1756. for (ASDisplayNode *subnode in node.subnodes) {
  1757. _recursivelySetDisplaySuspended(subnode, nil, flag);
  1758. }
  1759. }
  1760. }
  1761. - (BOOL)displaySuspended
  1762. {
  1763. ASDN::MutexLocker l(__instanceLock__);
  1764. return _flags.displaySuspended;
  1765. }
  1766. - (void)setDisplaySuspended:(BOOL)flag
  1767. {
  1768. ASDisplayNodeAssertThreadAffinity(self);
  1769. // Can't do this for synchronous nodes (using layers that are not _ASDisplayLayer and so we can't control display prevention/cancel)
  1770. if (_flags.synchronous)
  1771. return;
  1772. ASDN::MutexLocker l(__instanceLock__);
  1773. if (_flags.displaySuspended == flag)
  1774. return;
  1775. _flags.displaySuspended = flag;
  1776. self.asyncLayer.displaySuspended = flag;
  1777. if ([self __implementsDisplay]) {
  1778. // Display start and finish methods needs to happen on the main thread
  1779. ASPerformBlockOnMainThread(^{
  1780. if (flag) {
  1781. [_supernode subnodeDisplayDidFinish:self];
  1782. } else {
  1783. [_supernode subnodeDisplayWillStart:self];
  1784. }
  1785. });
  1786. }
  1787. }
  1788. NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority;
  1789. static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
  1790. - (void)setDrawingPriority:(NSInteger)drawingPriority
  1791. {
  1792. ASDisplayNodeAssertThreadAffinity(self);
  1793. ASDN::MutexLocker l(__instanceLock__);
  1794. if (drawingPriority == ASDefaultDrawingPriority) {
  1795. _flags.hasCustomDrawingPriority = NO;
  1796. objc_setAssociatedObject(self, ASDisplayNodeDrawingPriorityKey, nil, OBJC_ASSOCIATION_ASSIGN);
  1797. } else {
  1798. _flags.hasCustomDrawingPriority = YES;
  1799. objc_setAssociatedObject(self, ASDisplayNodeDrawingPriorityKey, @(drawingPriority), OBJC_ASSOCIATION_RETAIN);
  1800. }
  1801. }
  1802. - (NSInteger)drawingPriority
  1803. {
  1804. ASDisplayNodeAssertThreadAffinity(self);
  1805. ASDN::MutexLocker l(__instanceLock__);
  1806. if (!_flags.hasCustomDrawingPriority)
  1807. return ASDefaultDrawingPriority;
  1808. else
  1809. return [objc_getAssociatedObject(self, ASDisplayNodeDrawingPriorityKey) integerValue];
  1810. }
  1811. #pragma mark <_ASDisplayLayerDelegate>
  1812. - (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer asynchronously:(BOOL)asynchronously
  1813. {
  1814. // Subclass hook.
  1815. [self displayWillStart];
  1816. [self displayWillStartAsynchronously:asynchronously];
  1817. }
  1818. - (void)didDisplayAsyncLayer:(_ASDisplayLayer *)layer
  1819. {
  1820. // Subclass hook.
  1821. [self displayDidFinish];
  1822. }
  1823. - (void)displayWillStart {}
  1824. - (void)displayWillStartAsynchronously:(BOOL)asynchronously
  1825. {
  1826. [self displayWillStart]; // Subclass override
  1827. ASDisplayNodeAssertMainThread();
  1828. ASDisplayNodeLogEvent(self, @"displayWillStart");
  1829. // in case current node takes longer to display than it's subnodes, treat it as a dependent node
  1830. [self _pendingNodeWillDisplay:self];
  1831. [_supernode subnodeDisplayWillStart:self];
  1832. }
  1833. - (void)displayDidFinish
  1834. {
  1835. ASDisplayNodeAssertMainThread();
  1836. ASDisplayNodeLogEvent(self, @"displayDidFinish");
  1837. [self _pendingNodeDidDisplay:self];
  1838. [_supernode subnodeDisplayDidFinish:self];
  1839. }
  1840. - (void)subnodeDisplayWillStart:(ASDisplayNode *)subnode
  1841. {
  1842. [self _pendingNodeWillDisplay:subnode];
  1843. }
  1844. - (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode
  1845. {
  1846. [self _pendingNodeDidDisplay:subnode];
  1847. }
  1848. #pragma mark <CALayerDelegate>
  1849. // We are only the delegate for the layer when we are layer-backed, as UIView performs this funcition normally
  1850. - (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event
  1851. {
  1852. if (event == kCAOnOrderIn) {
  1853. [self __enterHierarchy];
  1854. } else if (event == kCAOnOrderOut) {
  1855. [self __exitHierarchy];
  1856. }
  1857. ASDisplayNodeAssert(_flags.layerBacked, @"We shouldn't get called back here if there is no layer");
  1858. return (id)kCFNull;
  1859. }
  1860. #pragma mark - Error Handling
  1861. + (void)setNonFatalErrorBlock:(ASDisplayNodeNonFatalErrorBlock)nonFatalErrorBlock
  1862. {
  1863. if (_nonFatalErrorBlock != nonFatalErrorBlock) {
  1864. _nonFatalErrorBlock = [nonFatalErrorBlock copy];
  1865. }
  1866. }
  1867. + (ASDisplayNodeNonFatalErrorBlock)nonFatalErrorBlock
  1868. {
  1869. return _nonFatalErrorBlock;
  1870. }
  1871. #pragma mark - Converting to and from the Node's Coordinate System
  1872. - (CATransform3D)_transformToAncestor:(ASDisplayNode *)ancestor
  1873. {
  1874. CATransform3D transform = CATransform3DIdentity;
  1875. ASDisplayNode *currentNode = self;
  1876. while (currentNode.supernode) {
  1877. if (currentNode == ancestor) {
  1878. return transform;
  1879. }
  1880. CGPoint anchorPoint = currentNode.anchorPoint;
  1881. CGRect bounds = currentNode.bounds;
  1882. CGPoint position = currentNode.position;
  1883. CGPoint origin = CGPointMake(position.x - bounds.size.width * anchorPoint.x,
  1884. position.y - bounds.size.height * anchorPoint.y);
  1885. transform = CATransform3DTranslate(transform, origin.x, origin.y, 0);
  1886. transform = CATransform3DTranslate(transform, -bounds.origin.x, -bounds.origin.y, 0);
  1887. currentNode = currentNode.supernode;
  1888. }
  1889. return transform;
  1890. }
  1891. static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNode *referenceNode, ASDisplayNode *targetNode)
  1892. {
  1893. ASDisplayNode *ancestor = ASDisplayNodeFindClosestCommonAncestor(referenceNode, targetNode);
  1894. // Transform into global (away from reference coordinate space)
  1895. CATransform3D transformToGlobal = [referenceNode _transformToAncestor:ancestor];
  1896. // Transform into local (via inverse transform from target to ancestor)
  1897. CATransform3D transformToLocal = CATransform3DInvert([targetNode _transformToAncestor:ancestor]);
  1898. return CATransform3DConcat(transformToGlobal, transformToLocal);
  1899. }
  1900. - (CGPoint)convertPoint:(CGPoint)point fromNode:(ASDisplayNode *)node
  1901. {
  1902. ASDisplayNodeAssertThreadAffinity(self);
  1903. /**
  1904. * When passed node=nil, all methods in this family use the UIView-style
  1905. * behavior – that is, convert from/to window coordinates if there's a window,
  1906. * otherwise return the point untransformed.
  1907. */
  1908. if (node == nil && self.nodeLoaded) {
  1909. CALayer *layer = self.layer;
  1910. if (UIWindow *window = ASFindWindowOfLayer(layer)) {
  1911. return [layer convertPoint:point fromLayer:window.layer];
  1912. } else {
  1913. return point;
  1914. }
  1915. }
  1916. // Get root node of the accessible node hierarchy, if node not specified
  1917. node = node ? : ASDisplayNodeUltimateParentOfNode(self);
  1918. // Calculate transform to map points between coordinate spaces
  1919. CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(node, self);
  1920. CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform);
  1921. ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform));
  1922. // Apply to point
  1923. return CGPointApplyAffineTransform(point, flattenedTransform);
  1924. }
  1925. - (CGPoint)convertPoint:(CGPoint)point toNode:(ASDisplayNode *)node
  1926. {
  1927. ASDisplayNodeAssertThreadAffinity(self);
  1928. if (node == nil && self.nodeLoaded) {
  1929. CALayer *layer = self.layer;
  1930. if (UIWindow *window = ASFindWindowOfLayer(layer)) {
  1931. return [layer convertPoint:point toLayer:window.layer];
  1932. } else {
  1933. return point;
  1934. }
  1935. }
  1936. // Get root node of the accessible node hierarchy, if node not specified
  1937. node = node ? : ASDisplayNodeUltimateParentOfNode(self);
  1938. // Calculate transform to map points between coordinate spaces
  1939. CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(self, node);
  1940. CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform);
  1941. ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform));
  1942. // Apply to point
  1943. return CGPointApplyAffineTransform(point, flattenedTransform);
  1944. }
  1945. - (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node
  1946. {
  1947. ASDisplayNodeAssertThreadAffinity(self);
  1948. if (node == nil && self.nodeLoaded) {
  1949. CALayer *layer = self.layer;
  1950. if (UIWindow *window = ASFindWindowOfLayer(layer)) {
  1951. return [layer convertRect:rect fromLayer:window.layer];
  1952. } else {
  1953. return rect;
  1954. }
  1955. }
  1956. // Get root node of the accessible node hierarchy, if node not specified
  1957. node = node ? : ASDisplayNodeUltimateParentOfNode(self);
  1958. // Calculate transform to map points between coordinate spaces
  1959. CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(node, self);
  1960. CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform);
  1961. ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform));
  1962. // Apply to rect
  1963. return CGRectApplyAffineTransform(rect, flattenedTransform);
  1964. }
  1965. - (CGRect)convertRect:(CGRect)rect toNode:(ASDisplayNode *)node
  1966. {
  1967. ASDisplayNodeAssertThreadAffinity(self);
  1968. if (node == nil && self.nodeLoaded) {
  1969. CALayer *layer = self.layer;
  1970. if (UIWindow *window = ASFindWindowOfLayer(layer)) {
  1971. return [layer convertRect:rect toLayer:window.layer];
  1972. } else {
  1973. return rect;
  1974. }
  1975. }
  1976. // Get root node of the accessible node hierarchy, if node not specified
  1977. node = node ? : ASDisplayNodeUltimateParentOfNode(self);
  1978. // Calculate transform to map points between coordinate spaces
  1979. CATransform3D nodeTransform = _calculateTransformFromReferenceToTarget(self, node);
  1980. CGAffineTransform flattenedTransform = CATransform3DGetAffineTransform(nodeTransform);
  1981. ASDisplayNodeAssertTrue(CATransform3DIsAffine(nodeTransform));
  1982. // Apply to rect
  1983. return CGRectApplyAffineTransform(rect, flattenedTransform);
  1984. }
  1985. #pragma mark - Managing the Node Hierarchy
  1986. ASDISPLAYNODE_INLINE bool shouldDisableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASDisplayNode *to) {
  1987. if (!from || !to) return NO;
  1988. if (from.isSynchronous) return NO;
  1989. if (to.isSynchronous) return NO;
  1990. if (from.isInHierarchy != to.isInHierarchy) return NO;
  1991. return YES;
  1992. }
  1993. /// Returns incremented value of i if i is not NSNotFound
  1994. ASDISPLAYNODE_INLINE NSInteger incrementIfFound(NSInteger i) {
  1995. return i == NSNotFound ? NSNotFound : i + 1;
  1996. }
  1997. /// Returns if a node is a member of a rasterized tree
  1998. ASDISPLAYNODE_INLINE BOOL canUseViewAPI(ASDisplayNode *node, ASDisplayNode *subnode) {
  1999. return (subnode.isLayerBacked == NO && node.isLayerBacked == NO);
  2000. }
  2001. /// Returns if node is a member of a rasterized tree
  2002. ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) {
  2003. return (node.shouldRasterizeDescendants || (node.hierarchyState & ASHierarchyStateRasterized));
  2004. }
  2005. // NOTE: This method must be dealloc-safe (should not retain self).
  2006. - (ASDisplayNode *)supernode
  2007. {
  2008. #if CHECK_LOCKING_SAFETY
  2009. if (__instanceLock__.ownedByCurrentThread()) {
  2010. NSLog(@"WARNING: Accessing supernode while holding recursive instance lock of this node is worrisome. It's likely that you will soon try to acquire the supernode's lock, and this can easily cause deadlocks.");
  2011. }
  2012. #endif
  2013. ASDN::MutexLocker l(__instanceLock__);
  2014. return _supernode;
  2015. }
  2016. - (void)__setSupernode:(ASDisplayNode *)newSupernode
  2017. {
  2018. BOOL supernodeDidChange = NO;
  2019. ASDisplayNode *oldSupernode = nil;
  2020. {
  2021. ASDN::MutexLocker l(__instanceLock__);
  2022. if (_supernode != newSupernode) {
  2023. oldSupernode = _supernode; // Access supernode properties outside of lock to avoid remote chance of deadlock,
  2024. // in case supernode implementation must access one of our properties.
  2025. _supernode = newSupernode;
  2026. supernodeDidChange = YES;
  2027. }
  2028. }
  2029. if (supernodeDidChange) {
  2030. ASDisplayNodeLogEvent(self, @"supernodeDidChange: %@, oldValue = %@", ASObjectDescriptionMakeTiny(newSupernode), ASObjectDescriptionMakeTiny(oldSupernode));
  2031. // Hierarchy state
  2032. ASHierarchyState stateToEnterOrExit = (newSupernode ? newSupernode.hierarchyState
  2033. : oldSupernode.hierarchyState);
  2034. // Rasterized state
  2035. BOOL parentWasOrIsRasterized = (newSupernode ? newSupernode.shouldRasterizeDescendants
  2036. : oldSupernode.shouldRasterizeDescendants);
  2037. if (parentWasOrIsRasterized) {
  2038. stateToEnterOrExit |= ASHierarchyStateRasterized;
  2039. }
  2040. if (newSupernode) {
  2041. [self enterHierarchyState:stateToEnterOrExit];
  2042. // If a node was added to a supernode, the supernode could be in a layout pending state. All of the hierarchy state
  2043. // properties related to the transition need to be copied over as well as propagated down the subtree.
  2044. // This is especially important as with automatic subnode management, adding subnodes can happen while a transition
  2045. // is in fly
  2046. if (ASHierarchyStateIncludesLayoutPending(stateToEnterOrExit)) {
  2047. int32_t pendingTransitionId = newSupernode.pendingTransitionID;
  2048. if (pendingTransitionId != ASLayoutElementContextInvalidTransitionID) {
  2049. {
  2050. ASDN::MutexLocker l(__instanceLock__);
  2051. _pendingTransitionID = pendingTransitionId;
  2052. // Propagate down the new pending transition id
  2053. ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
  2054. node.pendingTransitionID = pendingTransitionId;
  2055. });
  2056. }
  2057. }
  2058. }
  2059. // Now that we have a supernode, propagate its traits to self.
  2060. ASTraitCollectionPropagateDown(self, newSupernode.primitiveTraitCollection);
  2061. } else {
  2062. // If a node will be removed from the supernode it should go out from the layout pending state to remove all
  2063. // layout pending state related properties on the node
  2064. stateToEnterOrExit |= ASHierarchyStateLayoutPending;
  2065. [self exitHierarchyState:stateToEnterOrExit];
  2066. // We only need to explicitly exit hierarchy here if we were rasterized.
  2067. // Otherwise we will exit the hierarchy when our view/layer does so
  2068. // which has some nice carry-over machinery to handle cases where we are removed from a hierarchy
  2069. // and then added into it again shortly after.
  2070. if (parentWasOrIsRasterized && _flags.isInHierarchy) {
  2071. [self __exitHierarchy];
  2072. }
  2073. }
  2074. }
  2075. }
  2076. - (NSArray *)subnodes
  2077. {
  2078. ASDN::MutexLocker l(__instanceLock__);
  2079. return ([_subnodes copy] ?: @[]);
  2080. }
  2081. /*
  2082. * Central private helper method that should eventually be called if submethods add, insert or replace subnodes
  2083. * This method is called with thread affinity.
  2084. *
  2085. * @param subnode The subnode to insert
  2086. * @param subnodeIndex The index in _subnodes to insert it
  2087. * @param viewSublayerIndex The index in layer.sublayers (not view.subviews) at which to insert the view (use if we can use the view API) otherwise pass NSNotFound
  2088. * @param sublayerIndex The index in layer.sublayers at which to insert the layer (use if either parent or subnode is layer-backed) otherwise pass NSNotFound
  2089. * @param oldSubnode Remove this subnode before inserting; ok to be nil if no removal is desired
  2090. */
  2091. - (void)_insertSubnode:(ASDisplayNode *)subnode atSubnodeIndex:(NSInteger)subnodeIndex sublayerIndex:(NSInteger)sublayerIndex andRemoveSubnode:(ASDisplayNode *)oldSubnode
  2092. {
  2093. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2094. if (subnode == nil || subnode == self) {
  2095. ASDisplayNodeFailAssert(@"Cannot insert a nil subnode or self as subnode");
  2096. return;
  2097. }
  2098. if (subnodeIndex == NSNotFound) {
  2099. ASDisplayNodeFailAssert(@"Try to insert node on an index that was not found");
  2100. return;
  2101. }
  2102. if (self.layerBacked && !subnode.layerBacked) {
  2103. ASDisplayNodeFailAssert(@"Cannot add a view-backed node as a subnode of a layer-backed node. Supernode: %@, subnode: %@", self, subnode);
  2104. return;
  2105. }
  2106. __instanceLock__.lock();
  2107. NSUInteger subnodesCount = _subnodes.count;
  2108. __instanceLock__.unlock();
  2109. if (subnodeIndex > subnodesCount || subnodeIndex < 0) {
  2110. ASDisplayNodeFailAssert(@"Cannot insert a subnode at index %zd. Count is %zd", subnodeIndex, subnodesCount);
  2111. return;
  2112. }
  2113. // Disable appearance methods during move between supernodes, but make sure we restore their state after we do our thing
  2114. ASDisplayNode *oldParent = subnode.supernode;
  2115. BOOL disableNotifications = shouldDisableNotificationsForMovingBetweenParents(oldParent, self);
  2116. if (disableNotifications) {
  2117. [subnode __incrementVisibilityNotificationsDisabled];
  2118. }
  2119. [subnode _removeFromSupernode];
  2120. [oldSubnode _removeFromSupernode];
  2121. __instanceLock__.lock();
  2122. if (_subnodes == nil) {
  2123. _subnodes = [[NSMutableArray alloc] init];
  2124. }
  2125. [_subnodes insertObject:subnode atIndex:subnodeIndex];
  2126. __instanceLock__.unlock();
  2127. // This call will apply our .hierarchyState to the new subnode.
  2128. // If we are a managed hierarchy, as in ASCellNode trees, it will also apply our .interfaceState.
  2129. [subnode __setSupernode:self];
  2130. // If this subnode will be rasterized, update its hierarchy state & enter hierarchy if needed
  2131. if (nodeIsInRasterizedTree(self)) {
  2132. ASDisplayNodePerformBlockOnEveryNodeBFS(subnode, ^(ASDisplayNode * _Nonnull node) {
  2133. [node enterHierarchyState:ASHierarchyStateRasterized];
  2134. if (node.isNodeLoaded) {
  2135. [node __unloadNode];
  2136. }
  2137. });
  2138. if (self.isInHierarchy) {
  2139. [subnode __enterHierarchy];
  2140. }
  2141. } else if (self.nodeLoaded) {
  2142. // If not rasterizing, and node is loaded insert the subview/sublayer now.
  2143. [self _insertSubnodeSubviewOrSublayer:subnode atIndex:sublayerIndex];
  2144. } // Otherwise we will insert subview/sublayer when we get loaded
  2145. ASDisplayNodeAssert(disableNotifications == shouldDisableNotificationsForMovingBetweenParents(oldParent, self), @"Invariant violated");
  2146. if (disableNotifications) {
  2147. [subnode __decrementVisibilityNotificationsDisabled];
  2148. }
  2149. }
  2150. /*
  2151. * Inserts the view or layer of the given node at the given index
  2152. *
  2153. * @param subnode The subnode to insert
  2154. * @param idx The index in _view.subviews or _layer.sublayers at which to insert the subnode.view or
  2155. * subnode.layer of the subnode
  2156. */
  2157. - (void)_insertSubnodeSubviewOrSublayer:(ASDisplayNode *)subnode atIndex:(NSInteger)idx
  2158. {
  2159. ASDisplayNodeAssertMainThread();
  2160. ASDisplayNodeAssert(self.nodeLoaded, @"_insertSubnodeSubviewOrSublayer:atIndex: should never be called before our own view is created");
  2161. ASDisplayNodeAssert(idx != NSNotFound, @"Try to insert node on an index that was not found");
  2162. if (idx == NSNotFound) {
  2163. return;
  2164. }
  2165. // Because the view and layer can only be created and destroyed on Main, that is also the only thread
  2166. // where the view and layer can change. We can avoid locking.
  2167. // If we can use view API, do. Due to an apple bug, -insertSubview:atIndex: actually wants a LAYER index, which we pass in
  2168. if (canUseViewAPI(self, subnode)) {
  2169. [_view insertSubview:subnode.view atIndex:idx];
  2170. } else {
  2171. [_layer insertSublayer:subnode.layer atIndex:(unsigned int)idx];
  2172. }
  2173. }
  2174. - (void)addSubnode:(ASDisplayNode *)subnode
  2175. {
  2176. ASDisplayNodeLogEvent(self, @"addSubnode: %@", subnode);
  2177. // TODO: 2.0 Conversion: Reenable and fix within product code
  2178. //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually add subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode);
  2179. [self _addSubnode:subnode];
  2180. }
  2181. - (void)_addSubnode:(ASDisplayNode *)subnode
  2182. {
  2183. ASDisplayNodeAssertThreadAffinity(self);
  2184. ASDisplayNodeAssert(subnode, @"Cannot insert a nil subnode");
  2185. // Don't add if it's already a subnode
  2186. ASDisplayNode *oldParent = subnode.supernode;
  2187. if (!subnode || subnode == self || oldParent == self) {
  2188. return;
  2189. }
  2190. NSUInteger subnodesIndex;
  2191. NSUInteger sublayersIndex;
  2192. {
  2193. ASDN::MutexLocker l(__instanceLock__);
  2194. subnodesIndex = _subnodes.count;
  2195. sublayersIndex = _layer.sublayers.count;
  2196. }
  2197. [self _insertSubnode:subnode atSubnodeIndex:subnodesIndex sublayerIndex:sublayersIndex andRemoveSubnode:nil];
  2198. }
  2199. - (void)_addSubnodeViewsAndLayers
  2200. {
  2201. for (ASDisplayNode *node in [_subnodes copy]) {
  2202. [self _addSubnodeSubviewOrSublayer:node];
  2203. }
  2204. }
  2205. - (void)_addSubnodeSubviewOrSublayer:(ASDisplayNode *)subnode
  2206. {
  2207. // Due to a bug in Apple's framework we have to use the layer index to insert a subview
  2208. // so just use th ecount of the sublayers to add the subnode
  2209. NSInteger idx = _layer.sublayers.count;
  2210. [self _insertSubnodeSubviewOrSublayer:subnode atIndex:idx];
  2211. }
  2212. - (void)replaceSubnode:(ASDisplayNode *)oldSubnode withSubnode:(ASDisplayNode *)replacementSubnode
  2213. {
  2214. ASDisplayNodeLogEvent(self, @"replaceSubnode: %@ withSubnode:%@", oldSubnode, replacementSubnode);
  2215. // TODO: 2.0 Conversion: Reenable and fix within product code
  2216. //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually replace old node with replacement node to node with automaticallyManagesSubnodes=YES. Old Node: %@, replacement node: %@", oldSubnode, replacementSubnode);
  2217. [self _replaceSubnode:oldSubnode withSubnode:replacementSubnode];
  2218. }
  2219. - (void)_replaceSubnode:(ASDisplayNode *)oldSubnode withSubnode:(ASDisplayNode *)replacementSubnode
  2220. {
  2221. ASDisplayNodeAssertThreadAffinity(self);
  2222. if (replacementSubnode == nil) {
  2223. ASDisplayNodeFailAssert(@"Invalid subnode to replace");
  2224. return;
  2225. }
  2226. if (oldSubnode.supernode != self) {
  2227. ASDisplayNodeFailAssert(@"Old Subnode to replace must be a subnode");
  2228. return;
  2229. }
  2230. ASDisplayNodeAssert(!(self.nodeLoaded && !oldSubnode.nodeLoaded), @"We have view loaded, but child node does not.");
  2231. NSInteger subnodeIndex;
  2232. NSInteger sublayerIndex = NSNotFound;
  2233. {
  2234. ASDN::MutexLocker l(__instanceLock__);
  2235. ASDisplayNodeAssert(_subnodes, @"You should have subnodes if you have a subnode");
  2236. subnodeIndex = [_subnodes indexOfObjectIdenticalTo:oldSubnode];
  2237. // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the
  2238. // hierarchy and none of this could possibly work.
  2239. if (nodeIsInRasterizedTree(self) == NO) {
  2240. if (_layer) {
  2241. sublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:oldSubnode.layer];
  2242. ASDisplayNodeAssert(sublayerIndex != NSNotFound, @"Somehow oldSubnode's supernode is self, yet we could not find it in our layers to replace");
  2243. if (sublayerIndex == NSNotFound) {
  2244. return;
  2245. }
  2246. }
  2247. }
  2248. }
  2249. [self _insertSubnode:replacementSubnode atSubnodeIndex:subnodeIndex sublayerIndex:sublayerIndex andRemoveSubnode:oldSubnode];
  2250. }
  2251. - (void)insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below
  2252. {
  2253. ASDisplayNodeLogEvent(self, @"insertSubnode: %@ belowSubnode:%@", subnode, below);
  2254. // TODO: 2.0 Conversion: Reenable and fix within product code
  2255. //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode);
  2256. [self _insertSubnode:subnode belowSubnode:below];
  2257. }
  2258. - (void)_insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below
  2259. {
  2260. ASDisplayNodeAssertThreadAffinity(self);
  2261. if (subnode == nil) {
  2262. ASDisplayNodeFailAssert(@"Cannot insert a nil subnode");
  2263. return;
  2264. }
  2265. if (below.supernode != self) {
  2266. ASDisplayNodeFailAssert(@"Node to insert below must be a subnode");
  2267. return;
  2268. }
  2269. NSInteger belowSubnodeIndex;
  2270. NSInteger belowSublayerIndex = NSNotFound;
  2271. {
  2272. ASDN::MutexLocker l(__instanceLock__);
  2273. ASDisplayNodeAssert(_subnodes, @"You should have subnodes if you have a subnode");
  2274. belowSubnodeIndex = [_subnodes indexOfObjectIdenticalTo:below];
  2275. // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the
  2276. // hierarchy and none of this could possibly work.
  2277. if (nodeIsInRasterizedTree(self) == NO) {
  2278. if (_layer) {
  2279. belowSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:below.layer];
  2280. ASDisplayNodeAssert(belowSublayerIndex != NSNotFound, @"Somehow below's supernode is self, yet we could not find it in our layers to reference");
  2281. if (belowSublayerIndex == NSNotFound)
  2282. return;
  2283. }
  2284. ASDisplayNodeAssert(belowSubnodeIndex != NSNotFound, @"Couldn't find above in subnodes");
  2285. // If the subnode is already in the subnodes array / sublayers and it's before the below node, removing it to
  2286. // insert it will mess up our calculation
  2287. if (subnode.supernode == self) {
  2288. NSInteger currentIndexInSubnodes = [_subnodes indexOfObjectIdenticalTo:subnode];
  2289. if (currentIndexInSubnodes < belowSubnodeIndex) {
  2290. belowSubnodeIndex--;
  2291. }
  2292. if (_layer) {
  2293. NSInteger currentIndexInSublayers = [_layer.sublayers indexOfObjectIdenticalTo:subnode.layer];
  2294. if (currentIndexInSublayers < belowSublayerIndex) {
  2295. belowSublayerIndex--;
  2296. }
  2297. }
  2298. }
  2299. }
  2300. }
  2301. ASDisplayNodeAssert(belowSubnodeIndex != NSNotFound, @"Couldn't find below in subnodes");
  2302. [self _insertSubnode:subnode atSubnodeIndex:belowSubnodeIndex sublayerIndex:belowSublayerIndex andRemoveSubnode:nil];
  2303. }
  2304. - (void)insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above
  2305. {
  2306. ASDisplayNodeLogEvent(self, @"insertSubnode: %@ abodeSubnode: %@", subnode, above);
  2307. // TODO: 2.0 Conversion: Reenable and fix within product code
  2308. //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode);
  2309. [self _insertSubnode:subnode aboveSubnode:above];
  2310. }
  2311. - (void)_insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above
  2312. {
  2313. ASDisplayNodeAssertThreadAffinity(self);
  2314. if (subnode == nil) {
  2315. ASDisplayNodeFailAssert(@"Cannot insert a nil subnode");
  2316. return;
  2317. }
  2318. if (above.supernode != self) {
  2319. ASDisplayNodeFailAssert(@"Node to insert above must be a subnode");
  2320. return;
  2321. }
  2322. NSInteger aboveSubnodeIndex;
  2323. NSInteger aboveSublayerIndex = NSNotFound;
  2324. {
  2325. ASDN::MutexLocker l(__instanceLock__);
  2326. ASDisplayNodeAssert(_subnodes, @"You should have subnodes if you have a subnode");
  2327. aboveSubnodeIndex = [_subnodes indexOfObjectIdenticalTo:above];
  2328. // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the
  2329. // hierarchy and none of this could possibly work.
  2330. if (nodeIsInRasterizedTree(self) == NO) {
  2331. if (_layer) {
  2332. aboveSublayerIndex = [_layer.sublayers indexOfObjectIdenticalTo:above.layer];
  2333. ASDisplayNodeAssert(aboveSublayerIndex != NSNotFound, @"Somehow above's supernode is self, yet we could not find it in our layers to replace");
  2334. if (aboveSublayerIndex == NSNotFound)
  2335. return;
  2336. }
  2337. ASDisplayNodeAssert(aboveSubnodeIndex != NSNotFound, @"Couldn't find above in subnodes");
  2338. // If the subnode is already in the subnodes array / sublayers and it's before the below node, removing it to
  2339. // insert it will mess up our calculation
  2340. if (subnode.supernode == self) {
  2341. NSInteger currentIndexInSubnodes = [_subnodes indexOfObjectIdenticalTo:subnode];
  2342. if (currentIndexInSubnodes <= aboveSubnodeIndex) {
  2343. aboveSubnodeIndex--;
  2344. }
  2345. if (_layer) {
  2346. NSInteger currentIndexInSublayers = [_layer.sublayers indexOfObjectIdenticalTo:subnode.layer];
  2347. if (currentIndexInSublayers <= aboveSublayerIndex) {
  2348. aboveSublayerIndex--;
  2349. }
  2350. }
  2351. }
  2352. }
  2353. }
  2354. [self _insertSubnode:subnode atSubnodeIndex:incrementIfFound(aboveSubnodeIndex) sublayerIndex:incrementIfFound(aboveSublayerIndex) andRemoveSubnode:nil];
  2355. }
  2356. - (void)insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx
  2357. {
  2358. ASDisplayNodeLogEvent(self, @"insertSubnode: %@ atIndex: %td", subnode, idx);
  2359. // TODO: 2.0 Conversion: Reenable and fix within product code
  2360. //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode);
  2361. [self _insertSubnode:subnode atIndex:idx];
  2362. }
  2363. - (void)_insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx
  2364. {
  2365. ASDisplayNodeAssertThreadAffinity(self);
  2366. if (subnode == nil) {
  2367. ASDisplayNodeFailAssert(@"Cannot insert a nil subnode");
  2368. return;
  2369. }
  2370. NSInteger sublayerIndex = NSNotFound;
  2371. {
  2372. ASDN::MutexLocker l(__instanceLock__);
  2373. if (idx > _subnodes.count || idx < 0) {
  2374. ASDisplayNodeFailAssert(@"Cannot insert a subnode at index %zd. Count is %zd", idx, _subnodes.count);
  2375. return;
  2376. }
  2377. // Don't bother figuring out the sublayerIndex if in a rasterized subtree, because there are no layers in the
  2378. // hierarchy and none of this could possibly work.
  2379. if (nodeIsInRasterizedTree(self) == NO) {
  2380. // Account for potentially having other subviews
  2381. if (_layer && idx == 0) {
  2382. sublayerIndex = 0;
  2383. } else if (_layer) {
  2384. ASDisplayNode *positionInRelationTo = (_subnodes.count > 0 && idx > 0) ? _subnodes[idx - 1] : nil;
  2385. if (positionInRelationTo) {
  2386. sublayerIndex = incrementIfFound([_layer.sublayers indexOfObjectIdenticalTo:positionInRelationTo.layer]);
  2387. }
  2388. }
  2389. }
  2390. }
  2391. [self _insertSubnode:subnode atSubnodeIndex:idx sublayerIndex:sublayerIndex andRemoveSubnode:nil];
  2392. }
  2393. - (void)_removeSubnode:(ASDisplayNode *)subnode
  2394. {
  2395. ASDisplayNodeAssertThreadAffinity(self);
  2396. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2397. // Don't call self.supernode here because that will retain/autorelease the supernode. This method -_removeSupernode: is often called while tearing down a node hierarchy, and the supernode in question might be in the middle of its -dealloc. The supernode is never messaged, only compared by value, so this is safe.
  2398. // The particular issue that triggers this edge case is when a node calls -removeFromSupernode on a subnode from within its own -dealloc method.
  2399. if (!subnode || subnode.supernode != self) {
  2400. return;
  2401. }
  2402. __instanceLock__.lock();
  2403. [_subnodes removeObjectIdenticalTo:subnode];
  2404. __instanceLock__.unlock();
  2405. [subnode __setSupernode:nil];
  2406. }
  2407. - (void)removeFromSupernode
  2408. {
  2409. // TODO: 2.0 Conversion: Reenable and fix within product code
  2410. //ASDisplayNodeAssert(self.supernode.automaticallyManagesSubnodes == NO, @"Attempt to manually remove subnode from node with automaticallyManagesSubnodes=YES. Node: %@", self);
  2411. [self _removeFromSupernode];
  2412. }
  2413. // NOTE: You must not called this method while holding the receiver's instance lock. This may cause deadlocks.
  2414. - (void)_removeFromSupernode
  2415. {
  2416. ASDisplayNodeAssertThreadAffinity(self);
  2417. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2418. __instanceLock__.lock();
  2419. __weak ASDisplayNode *supernode = _supernode;
  2420. __weak UIView *view = _view;
  2421. __weak CALayer *layer = _layer;
  2422. __instanceLock__.unlock();
  2423. // Clear supernode's reference to us before removing the view from the hierarchy, as _ASDisplayView
  2424. // will trigger us to clear our _supernode pointer in willMoveToSuperview:nil.
  2425. // This may result in removing the last strong reference, triggering deallocation after this method.
  2426. [supernode _removeSubnode:self];
  2427. if (view != nil) {
  2428. [view removeFromSuperview];
  2429. } else if (layer != nil) {
  2430. [layer removeFromSuperlayer];
  2431. }
  2432. }
  2433. #pragma mark - Visibility API
  2434. - (BOOL)__visibilityNotificationsDisabled
  2435. {
  2436. // Currently, this method is only used by the testing infrastructure to verify this internal feature.
  2437. ASDN::MutexLocker l(__instanceLock__);
  2438. return _flags.visibilityNotificationsDisabled > 0;
  2439. }
  2440. - (BOOL)__selfOrParentHasVisibilityNotificationsDisabled
  2441. {
  2442. ASDN::MutexLocker l(__instanceLock__);
  2443. return (_hierarchyState & ASHierarchyStateTransitioningSupernodes);
  2444. }
  2445. - (void)__incrementVisibilityNotificationsDisabled
  2446. {
  2447. ASDN::MutexLocker l(__instanceLock__);
  2448. const size_t maxVisibilityIncrement = (1ULL<<VISIBILITY_NOTIFICATIONS_DISABLED_BITS) - 1ULL;
  2449. ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled < maxVisibilityIncrement, @"Oops, too many increments of the visibility notifications API");
  2450. if (_flags.visibilityNotificationsDisabled < maxVisibilityIncrement) {
  2451. _flags.visibilityNotificationsDisabled++;
  2452. }
  2453. if (_flags.visibilityNotificationsDisabled == 1) {
  2454. // Must have just transitioned from 0 to 1. Notify all subnodes that we are in a disabled state.
  2455. [self enterHierarchyState:ASHierarchyStateTransitioningSupernodes];
  2456. }
  2457. }
  2458. - (void)__decrementVisibilityNotificationsDisabled
  2459. {
  2460. ASDN::MutexLocker l(__instanceLock__);
  2461. ASDisplayNodeAssert(_flags.visibilityNotificationsDisabled > 0, @"Can't decrement past 0");
  2462. if (_flags.visibilityNotificationsDisabled > 0) {
  2463. _flags.visibilityNotificationsDisabled--;
  2464. }
  2465. if (_flags.visibilityNotificationsDisabled == 0) {
  2466. // Must have just transitioned from 1 to 0. Notify all subnodes that we are no longer in a disabled state.
  2467. // FIXME: This system should be revisited when refactoring and consolidating the implementation of the
  2468. // addSubnode: and insertSubnode:... methods. As implemented, though logically irrelevant for expected use cases,
  2469. // multiple nodes in the subtree below may have a non-zero visibilityNotification count and still have
  2470. // the ASHierarchyState bit cleared (the only value checked when reading this state).
  2471. [self exitHierarchyState:ASHierarchyStateTransitioningSupernodes];
  2472. }
  2473. }
  2474. #pragma mark - Placeholder
  2475. - (void)_locked_layoutPlaceholderIfNecessary
  2476. {
  2477. if ([self _shouldHavePlaceholderLayer]) {
  2478. [self _setupPlaceholderLayerIfNeeded];
  2479. }
  2480. // Update the placeholderLayer size in case the node size has changed since the placeholder was added.
  2481. _placeholderLayer.frame = self.threadSafeBounds;
  2482. }
  2483. - (BOOL)_shouldHavePlaceholderLayer
  2484. {
  2485. return (_placeholderEnabled && [self __implementsDisplay]);
  2486. }
  2487. - (void)_setupPlaceholderLayerIfNeeded
  2488. {
  2489. ASDisplayNodeAssertMainThread();
  2490. if (!_placeholderLayer) {
  2491. _placeholderLayer = [CALayer layer];
  2492. // do not set to CGFLOAT_MAX in the case that something needs to be overtop the placeholder
  2493. _placeholderLayer.zPosition = 9999.0;
  2494. }
  2495. if (_placeholderLayer.contents == nil) {
  2496. if (!_placeholderImage) {
  2497. _placeholderImage = [self placeholderImage];
  2498. }
  2499. if (_placeholderImage) {
  2500. BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(_placeholderImage.capInsets, UIEdgeInsetsZero);
  2501. if (stretchable) {
  2502. ASDisplayNodeSetupLayerContentsWithResizableImage(_placeholderLayer, _placeholderImage);
  2503. } else {
  2504. _placeholderLayer.contentsScale = self.contentsScale;
  2505. _placeholderLayer.contents = (id)_placeholderImage.CGImage;
  2506. }
  2507. }
  2508. }
  2509. }
  2510. - (UIImage *)placeholderImage
  2511. {
  2512. // Subclass hook
  2513. return nil;
  2514. }
  2515. - (BOOL)placeholderShouldPersist
  2516. {
  2517. // Subclass hook
  2518. return NO;
  2519. }
  2520. #pragma mark - Hierarchy State
  2521. - (BOOL)isInHierarchy
  2522. {
  2523. ASDN::MutexLocker l(__instanceLock__);
  2524. return _flags.isInHierarchy;
  2525. }
  2526. - (void)__enterHierarchy
  2527. {
  2528. ASDisplayNodeAssertMainThread();
  2529. ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"Should not cause recursive __enterHierarchy");
  2530. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2531. ASDisplayNodeLogEvent(self, @"enterHierarchy");
  2532. // Profiling has shown that locking this method is beneficial, so each of the property accesses don't have to lock and unlock.
  2533. __instanceLock__.lock();
  2534. if (!_flags.isInHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) {
  2535. _flags.isEnteringHierarchy = YES;
  2536. _flags.isInHierarchy = YES;
  2537. // Don't call -willEnterHierarchy while holding __instanceLock__.
  2538. // This method and subsequent ones (i.e -interfaceState and didEnter(.*)State)
  2539. // don't expect that they are called while the lock is being held.
  2540. // More importantly, didEnter(.*)State methods are meant to be overriden by clients.
  2541. // And so they can potentially walk up the node tree and cause deadlocks, or do expensive tasks and cause the lock to be held for too long.
  2542. __instanceLock__.unlock();
  2543. [self willEnterHierarchy];
  2544. for (ASDisplayNode *subnode in self.subnodes) {
  2545. [subnode __enterHierarchy];
  2546. }
  2547. __instanceLock__.lock();
  2548. _flags.isEnteringHierarchy = NO;
  2549. // If we don't have contents finished drawing by the time we are on screen, immediately add the placeholder (if it is enabled and we do have something to draw).
  2550. if (self.contents == nil) {
  2551. CALayer *layer = self.layer;
  2552. [layer setNeedsDisplay];
  2553. if ([self _shouldHavePlaceholderLayer]) {
  2554. [CATransaction begin];
  2555. [CATransaction setDisableActions:YES];
  2556. [self _setupPlaceholderLayerIfNeeded];
  2557. _placeholderLayer.opacity = 1.0;
  2558. [CATransaction commit];
  2559. [layer addSublayer:_placeholderLayer];
  2560. }
  2561. }
  2562. }
  2563. __instanceLock__.unlock();
  2564. }
  2565. - (void)__exitHierarchy
  2566. {
  2567. ASDisplayNodeAssertMainThread();
  2568. ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"Should not cause recursive __exitHierarchy");
  2569. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2570. ASDisplayNodeLogEvent(self, @"exitHierarchy");
  2571. // Profiling has shown that locking this method is beneficial, so each of the property accesses don't have to lock and unlock.
  2572. __instanceLock__.lock();
  2573. if (_flags.isInHierarchy && !_flags.visibilityNotificationsDisabled && ![self __selfOrParentHasVisibilityNotificationsDisabled]) {
  2574. _flags.isExitingHierarchy = YES;
  2575. _flags.isInHierarchy = NO;
  2576. [self.asyncLayer cancelAsyncDisplay];
  2577. // Don't call -didExitHierarchy while holding __instanceLock__.
  2578. // This method and subsequent ones (i.e -interfaceState and didExit(.*)State)
  2579. // don't expect that they are called while the lock is being held.
  2580. // More importantly, didExit(.*)State methods are meant to be overriden by clients.
  2581. // And so they can potentially walk up the node tree and cause deadlocks, or do expensive tasks and cause the lock to be held for too long.
  2582. __instanceLock__.unlock();
  2583. [self didExitHierarchy];
  2584. for (ASDisplayNode *subnode in self.subnodes) {
  2585. [subnode __exitHierarchy];
  2586. }
  2587. __instanceLock__.lock();
  2588. _flags.isExitingHierarchy = NO;
  2589. }
  2590. __instanceLock__.unlock();
  2591. }
  2592. - (void)enterHierarchyState:(ASHierarchyState)hierarchyState
  2593. {
  2594. if (hierarchyState == ASHierarchyStateNormal) {
  2595. return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing.
  2596. }
  2597. ASDisplayNodePerformBlockOnEveryNode(nil, self, NO, ^(ASDisplayNode *node) {
  2598. node.hierarchyState |= hierarchyState;
  2599. });
  2600. }
  2601. - (void)exitHierarchyState:(ASHierarchyState)hierarchyState
  2602. {
  2603. if (hierarchyState == ASHierarchyStateNormal) {
  2604. return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing.
  2605. }
  2606. ASDisplayNodePerformBlockOnEveryNode(nil, self, NO, ^(ASDisplayNode *node) {
  2607. node.hierarchyState &= (~hierarchyState);
  2608. });
  2609. }
  2610. - (ASHierarchyState)hierarchyState
  2611. {
  2612. ASDN::MutexLocker l(__instanceLock__);
  2613. return _hierarchyState;
  2614. }
  2615. - (void)setHierarchyState:(ASHierarchyState)newState
  2616. {
  2617. ASHierarchyState oldState = ASHierarchyStateNormal;
  2618. {
  2619. ASDN::MutexLocker l(__instanceLock__);
  2620. if (_hierarchyState == newState) {
  2621. return;
  2622. }
  2623. oldState = _hierarchyState;
  2624. _hierarchyState = newState;
  2625. }
  2626. // Entered rasterization state.
  2627. if (newState & ASHierarchyStateRasterized) {
  2628. ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be added to subtree of node with shouldRasterizeDescendants=YES. Node: %@", self);
  2629. }
  2630. // Entered or exited range managed state.
  2631. if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) {
  2632. if (newState & ASHierarchyStateRangeManaged) {
  2633. [self enterInterfaceState:self.supernode.interfaceState];
  2634. } else {
  2635. // The case of exiting a range-managed state should be fairly rare. Adding or removing the node
  2636. // to a view hierarchy will cause its interfaceState to be either fully set or unset (all fields),
  2637. // but because we might be about to be added to a view hierarchy, exiting the interface state now
  2638. // would cause inefficient churn. The tradeoff is that we may not clear contents / fetched data
  2639. // for nodes that are removed from a managed state and then retained but not used (bad idea anyway!)
  2640. }
  2641. }
  2642. if ((newState & ASHierarchyStateLayoutPending) != (oldState & ASHierarchyStateLayoutPending)) {
  2643. if (newState & ASHierarchyStateLayoutPending) {
  2644. // Entering layout pending state
  2645. } else {
  2646. // Leaving layout pending state, reset related properties
  2647. ASDN::MutexLocker l(__instanceLock__);
  2648. _pendingTransitionID = ASLayoutElementContextInvalidTransitionID;
  2649. _pendingLayoutTransition = nil;
  2650. }
  2651. }
  2652. ASDisplayNodeLogEvent(self, @"setHierarchyState: oldState = %@, newState = %@", NSStringFromASHierarchyState(oldState), NSStringFromASHierarchyState(newState));
  2653. }
  2654. - (void)willEnterHierarchy
  2655. {
  2656. ASDisplayNodeAssertMainThread();
  2657. ASDisplayNodeAssert(_flags.isEnteringHierarchy, @"You should never call -willEnterHierarchy directly. Appearance is automatically managed by ASDisplayNode");
  2658. ASDisplayNodeAssert(!_flags.isExitingHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
  2659. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2660. if (![self supportsRangeManagedInterfaceState]) {
  2661. self.interfaceState = ASInterfaceStateInHierarchy;
  2662. }
  2663. }
  2664. - (void)didExitHierarchy
  2665. {
  2666. ASDisplayNodeAssertMainThread();
  2667. ASDisplayNodeAssert(_flags.isExitingHierarchy, @"You should never call -didExitHierarchy directly. Appearance is automatically managed by ASDisplayNode");
  2668. ASDisplayNodeAssert(!_flags.isEnteringHierarchy, @"ASDisplayNode inconsistency. __enterHierarchy and __exitHierarchy are mutually exclusive");
  2669. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2670. if (![self supportsRangeManagedInterfaceState]) {
  2671. self.interfaceState = ASInterfaceStateNone;
  2672. } else {
  2673. // This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for
  2674. // things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail.
  2675. // Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the
  2676. // same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed).
  2677. // TODO: This approach could be optimized by only performing the dispatch for root elements + recursively apply the interface state change. This would require a closer
  2678. // integration with _ASDisplayLayer to ensure that the superlayer pointer has been cleared by this stage (to check if we are root or not), or a different delegate call.
  2679. if (ASInterfaceStateIncludesVisible(self.interfaceState)) {
  2680. dispatch_async(dispatch_get_main_queue(), ^{
  2681. // This block intentionally retains self.
  2682. __instanceLock__.lock();
  2683. unsigned isInHierarchy = _flags.isInHierarchy;
  2684. BOOL isVisible = ASInterfaceStateIncludesVisible(_interfaceState);
  2685. ASInterfaceState newState = (_interfaceState & ~ASInterfaceStateVisible);
  2686. __instanceLock__.unlock();
  2687. if (!isInHierarchy && isVisible) {
  2688. self.interfaceState = newState;
  2689. }
  2690. });
  2691. }
  2692. }
  2693. }
  2694. - (void)_recursiveWillEnterHierarchy
  2695. {
  2696. if (_flags.visibilityNotificationsDisabled) {
  2697. return;
  2698. }
  2699. _flags.isEnteringHierarchy = YES;
  2700. [self willEnterHierarchy];
  2701. _flags.isEnteringHierarchy = NO;
  2702. for (ASDisplayNode *subnode in self.subnodes) {
  2703. [subnode _recursiveWillEnterHierarchy];
  2704. }
  2705. }
  2706. - (void)_recursiveDidExitHierarchy
  2707. {
  2708. if (_flags.visibilityNotificationsDisabled) {
  2709. return;
  2710. }
  2711. _flags.isExitingHierarchy = YES;
  2712. [self didExitHierarchy];
  2713. _flags.isExitingHierarchy = NO;
  2714. for (ASDisplayNode *subnode in self.subnodes) {
  2715. [subnode _recursiveDidExitHierarchy];
  2716. }
  2717. }
  2718. #pragma mark - Interface State
  2719. /**
  2720. * We currently only set interface state on nodes in table/collection views. For other nodes, if they are
  2721. * in the hierarchy we enable all ASInterfaceState types with `ASInterfaceStateInHierarchy`, otherwise `None`.
  2722. */
  2723. - (BOOL)supportsRangeManagedInterfaceState
  2724. {
  2725. ASDN::MutexLocker l(__instanceLock__);
  2726. return ASHierarchyStateIncludesRangeManaged(_hierarchyState);
  2727. }
  2728. - (void)enterInterfaceState:(ASInterfaceState)interfaceState
  2729. {
  2730. if (interfaceState == ASInterfaceStateNone) {
  2731. return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing.
  2732. }
  2733. ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) {
  2734. node.interfaceState |= interfaceState;
  2735. });
  2736. }
  2737. - (void)exitInterfaceState:(ASInterfaceState)interfaceState
  2738. {
  2739. if (interfaceState == ASInterfaceStateNone) {
  2740. return; // This method is a no-op with a 0-bitfield argument, so don't bother recursing.
  2741. }
  2742. ASDisplayNodeLogEvent(self, @"%@ %@", NSStringFromSelector(_cmd), NSStringFromASInterfaceState(interfaceState));
  2743. ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) {
  2744. node.interfaceState &= (~interfaceState);
  2745. });
  2746. }
  2747. - (void)recursivelySetInterfaceState:(ASInterfaceState)newInterfaceState
  2748. {
  2749. // Instead of each node in the recursion assuming it needs to schedule itself for display,
  2750. // setInterfaceState: skips this when handling range-managed nodes (our whole subtree has this set).
  2751. // If our range manager intends for us to be displayed right now, and didn't before, get started!
  2752. BOOL shouldScheduleDisplay = [self supportsRangeManagedInterfaceState] && [self shouldScheduleDisplayWithNewInterfaceState:newInterfaceState];
  2753. ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode *node) {
  2754. node.interfaceState = newInterfaceState;
  2755. });
  2756. if (shouldScheduleDisplay) {
  2757. [ASDisplayNode scheduleNodeForRecursiveDisplay:self];
  2758. }
  2759. }
  2760. - (ASInterfaceState)interfaceState
  2761. {
  2762. ASDN::MutexLocker l(__instanceLock__);
  2763. return _interfaceState;
  2764. }
  2765. - (void)setInterfaceState:(ASInterfaceState)newState
  2766. {
  2767. //This method is currently called on the main thread. The assert has been added here because all of the
  2768. //did(Enter|Exit)(Display|Visible|Preload)State methods currently guarantee calling on main.
  2769. ASDisplayNodeAssertMainThread();
  2770. // It should never be possible for a node to be visible but not be allowed / expected to display.
  2771. ASDisplayNodeAssertFalse(ASInterfaceStateIncludesVisible(newState) && !ASInterfaceStateIncludesDisplay(newState));
  2772. // This method manages __instanceLock__ itself, to ensure the lock is not held while didEnter/Exit(.*)State methods are called, thus avoid potential deadlocks
  2773. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2774. ASInterfaceState oldState = ASInterfaceStateNone;
  2775. {
  2776. ASDN::MutexLocker l(__instanceLock__);
  2777. if (_interfaceState == newState) {
  2778. return;
  2779. }
  2780. oldState = _interfaceState;
  2781. _interfaceState = newState;
  2782. }
  2783. // TODO: Trigger asynchronous measurement if it is not already cached or being calculated.
  2784. // if ((newState & ASInterfaceStateMeasureLayout) != (oldState & ASInterfaceStateMeasureLayout)) {
  2785. // }
  2786. // For the Preload and Display ranges, we don't want to call -clear* if not being managed by a range controller.
  2787. // Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop.
  2788. // Still, the interfaceState should be updated to the current state of the node; just don't act on the transition.
  2789. // Entered or exited data loading state.
  2790. BOOL nowPreload = ASInterfaceStateIncludesPreload(newState);
  2791. BOOL wasPreload = ASInterfaceStateIncludesPreload(oldState);
  2792. if (nowPreload != wasPreload) {
  2793. if (nowPreload) {
  2794. [self didEnterPreloadState];
  2795. } else {
  2796. // We don't want to call -didExitPreloadState on nodes that aren't being managed by a range controller.
  2797. // Otherwise we get flashing behavior from normal UIKit manipulations like navigation controller push / pop.
  2798. if ([self supportsRangeManagedInterfaceState]) {
  2799. [self didExitPreloadState];
  2800. }
  2801. }
  2802. }
  2803. // Entered or exited contents rendering state.
  2804. BOOL nowDisplay = ASInterfaceStateIncludesDisplay(newState);
  2805. BOOL wasDisplay = ASInterfaceStateIncludesDisplay(oldState);
  2806. if (nowDisplay != wasDisplay) {
  2807. if ([self supportsRangeManagedInterfaceState]) {
  2808. if (nowDisplay) {
  2809. // Once the working window is eliminated (ASRangeHandlerRender), trigger display directly here.
  2810. [self setDisplaySuspended:NO];
  2811. } else {
  2812. [self setDisplaySuspended:YES];
  2813. //schedule clear contents on next runloop
  2814. dispatch_async(dispatch_get_main_queue(), ^{
  2815. ASDN::MutexLocker l(__instanceLock__);
  2816. if (ASInterfaceStateIncludesDisplay(_interfaceState) == NO) {
  2817. [self clearContents];
  2818. }
  2819. });
  2820. }
  2821. } else {
  2822. // NOTE: This case isn't currently supported as setInterfaceState: isn't exposed externally, and all
  2823. // internal use cases are range-managed. When a node is visible, don't mess with display - CA will start it.
  2824. if (!ASInterfaceStateIncludesVisible(newState)) {
  2825. // Check __implementsDisplay purely for efficiency - it's faster even than calling -asyncLayer.
  2826. if ([self __implementsDisplay]) {
  2827. if (nowDisplay) {
  2828. [ASDisplayNode scheduleNodeForRecursiveDisplay:self];
  2829. } else {
  2830. [[self asyncLayer] cancelAsyncDisplay];
  2831. //schedule clear contents on next runloop
  2832. dispatch_async(dispatch_get_main_queue(), ^{
  2833. ASDN::MutexLocker l(__instanceLock__);
  2834. if (ASInterfaceStateIncludesDisplay(_interfaceState) == NO) {
  2835. [self clearContents];
  2836. }
  2837. });
  2838. }
  2839. }
  2840. }
  2841. }
  2842. if (nowDisplay) {
  2843. [self didEnterDisplayState];
  2844. } else {
  2845. [self didExitDisplayState];
  2846. }
  2847. }
  2848. // Became visible or invisible. When range-managed, this represents literal visibility - at least one pixel
  2849. // is onscreen. If not range-managed, we can't guarantee more than the node being present in an onscreen window.
  2850. BOOL nowVisible = ASInterfaceStateIncludesVisible(newState);
  2851. BOOL wasVisible = ASInterfaceStateIncludesVisible(oldState);
  2852. if (nowVisible != wasVisible) {
  2853. if (nowVisible) {
  2854. [self didEnterVisibleState];
  2855. } else {
  2856. [self didExitVisibleState];
  2857. }
  2858. }
  2859. ASDisplayNodeLogEvent(self, @"interfaceStateDidChange: %@, old: %@", NSStringFromASInterfaceState(newState), NSStringFromASInterfaceState(oldState));
  2860. [self interfaceStateDidChange:newState fromState:oldState];
  2861. }
  2862. - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
  2863. {
  2864. // Subclass hook
  2865. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2866. [_interfaceStateDelegate interfaceStateDidChange:newState fromState:oldState];
  2867. }
  2868. - (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState
  2869. {
  2870. BOOL willDisplay = ASInterfaceStateIncludesDisplay(newInterfaceState);
  2871. BOOL nowDisplay = ASInterfaceStateIncludesDisplay(self.interfaceState);
  2872. return willDisplay && (willDisplay != nowDisplay);
  2873. }
  2874. - (BOOL)isVisible
  2875. {
  2876. ASDN::MutexLocker l(__instanceLock__);
  2877. return ASInterfaceStateIncludesVisible(_interfaceState);
  2878. }
  2879. - (void)didEnterVisibleState
  2880. {
  2881. // subclass override
  2882. ASDisplayNodeAssertMainThread();
  2883. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2884. [_interfaceStateDelegate didEnterVisibleState];
  2885. }
  2886. - (void)didExitVisibleState
  2887. {
  2888. // subclass override
  2889. ASDisplayNodeAssertMainThread();
  2890. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2891. [_interfaceStateDelegate didExitVisibleState];
  2892. }
  2893. - (BOOL)isInDisplayState
  2894. {
  2895. ASDN::MutexLocker l(__instanceLock__);
  2896. return ASInterfaceStateIncludesDisplay(_interfaceState);
  2897. }
  2898. - (void)didEnterDisplayState
  2899. {
  2900. // subclass override
  2901. ASDisplayNodeAssertMainThread();
  2902. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2903. [_interfaceStateDelegate didEnterDisplayState];
  2904. }
  2905. - (void)didExitDisplayState
  2906. {
  2907. // subclass override
  2908. ASDisplayNodeAssertMainThread();
  2909. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2910. [_interfaceStateDelegate didExitDisplayState];
  2911. }
  2912. - (BOOL)isInPreloadState
  2913. {
  2914. ASDN::MutexLocker l(__instanceLock__);
  2915. return ASInterfaceStateIncludesPreload(_interfaceState);
  2916. }
  2917. - (void)setNeedsPreload
  2918. {
  2919. if (self.isInPreloadState) {
  2920. [self recursivelyPreload];
  2921. }
  2922. }
  2923. - (void)recursivelyPreload
  2924. {
  2925. ASPerformBlockOnMainThread(^{
  2926. ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) {
  2927. [node didEnterPreloadState];
  2928. });
  2929. });
  2930. }
  2931. - (void)recursivelyClearPreloadedData
  2932. {
  2933. ASPerformBlockOnMainThread(^{
  2934. ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) {
  2935. [node didExitPreloadState];
  2936. });
  2937. });
  2938. }
  2939. - (void)didEnterPreloadState
  2940. {
  2941. ASDisplayNodeAssertMainThread();
  2942. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2943. [_interfaceStateDelegate didEnterPreloadState];
  2944. if (_methodOverrides & ASDisplayNodeMethodOverrideFetchData) {
  2945. #pragma clang diagnostic push
  2946. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  2947. [self fetchData];
  2948. #pragma clang diagnostic pop
  2949. }
  2950. }
  2951. - (void)didExitPreloadState
  2952. {
  2953. ASDisplayNodeAssertMainThread();
  2954. ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__);
  2955. [_interfaceStateDelegate didExitPreloadState];
  2956. if (_methodOverrides & ASDisplayNodeMethodOverrideClearFetchedData) {
  2957. #pragma clang diagnostic push
  2958. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  2959. [self clearFetchedData];
  2960. #pragma clang diagnostic pop
  2961. }
  2962. }
  2963. - (void)clearContents
  2964. {
  2965. ASDisplayNodeAssertMainThread();
  2966. if (_flags.canClearContentsOfLayer) {
  2967. // No-op if these haven't been created yet, as that guarantees they don't have contents that needs to be released.
  2968. _layer.contents = nil;
  2969. }
  2970. _placeholderLayer.contents = nil;
  2971. _placeholderImage = nil;
  2972. }
  2973. - (void)recursivelyClearContents
  2974. {
  2975. ASPerformBlockOnMainThread(^{
  2976. ASDisplayNodePerformBlockOnEveryNode(nil, self, YES, ^(ASDisplayNode * _Nonnull node) {
  2977. [node clearContents];
  2978. });
  2979. });
  2980. }
  2981. #pragma mark - Gesture Recognizing
  2982. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
  2983. {
  2984. // Subclass hook
  2985. }
  2986. - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
  2987. {
  2988. // Subclass hook
  2989. }
  2990. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
  2991. {
  2992. // Subclass hook
  2993. }
  2994. - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
  2995. {
  2996. // Subclass hook
  2997. }
  2998. - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
  2999. {
  3000. // This method is only implemented on UIView on iOS 6+.
  3001. ASDisplayNodeAssertMainThread();
  3002. if (!_view)
  3003. return YES;
  3004. // If we reach the base implementation, forward up the view hierarchy.
  3005. UIView *superview = _view.superview;
  3006. return [superview gestureRecognizerShouldBegin:gestureRecognizer];
  3007. }
  3008. - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
  3009. {
  3010. ASDisplayNodeAssertMainThread();
  3011. return [_view hitTest:point withEvent:event];
  3012. }
  3013. - (void)setHitTestSlop:(UIEdgeInsets)hitTestSlop
  3014. {
  3015. ASDN::MutexLocker l(__instanceLock__);
  3016. _hitTestSlop = hitTestSlop;
  3017. }
  3018. - (UIEdgeInsets)hitTestSlop
  3019. {
  3020. ASDN::MutexLocker l(__instanceLock__);
  3021. return _hitTestSlop;
  3022. }
  3023. - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
  3024. {
  3025. ASDisplayNodeAssertMainThread();
  3026. UIEdgeInsets slop = self.hitTestSlop;
  3027. if (_view && UIEdgeInsetsEqualToEdgeInsets(slop, UIEdgeInsetsZero)) {
  3028. // Safer to use UIView's -pointInside:withEvent: if we can.
  3029. return [_view pointInside:point withEvent:event];
  3030. } else {
  3031. return CGRectContainsPoint(UIEdgeInsetsInsetRect(self.bounds, slop), point);
  3032. }
  3033. }
  3034. #pragma mark - Pending View State
  3035. - (void)_applyPendingStateToViewOrLayer
  3036. {
  3037. ASDisplayNodeAssertMainThread();
  3038. ASDisplayNodeAssert(self.nodeLoaded, @"must have a view or layer");
  3039. // If no view/layer properties were set before the view/layer were created, _pendingViewState will be nil and the default values
  3040. // for the view/layer are still valid.
  3041. ASDN::MutexLocker l(__instanceLock__);
  3042. [self applyPendingViewState];
  3043. // TODO: move this into real pending state
  3044. if (_flags.displaySuspended) {
  3045. self.asyncLayer.displaySuspended = YES;
  3046. }
  3047. if (!_flags.displaysAsynchronously) {
  3048. self.asyncLayer.displaysAsynchronously = NO;
  3049. }
  3050. }
  3051. - (void)applyPendingViewState
  3052. {
  3053. ASDisplayNodeAssertMainThread();
  3054. ASDN::MutexLocker l(__instanceLock__);
  3055. // FIXME: Ideally we'd call this as soon as the node receives -setNeedsLayout
  3056. // but automatic subnode management would require us to modify the node tree
  3057. // in the background on a loaded node, which isn't currently supported.
  3058. if (_pendingViewState.hasSetNeedsLayout) {
  3059. //Need to unlock before calling setNeedsLayout to avoid deadlocks.
  3060. //MutexUnlocker will re-lock at the end of scope.
  3061. ASDN::MutexUnlocker u(__instanceLock__);
  3062. [self __setNeedsLayout];
  3063. }
  3064. if (self.layerBacked) {
  3065. [_pendingViewState applyToLayer:self.layer];
  3066. } else {
  3067. BOOL specialPropertiesHandling = ASDisplayNodeNeedsSpecialPropertiesHandlingForFlags(_flags);
  3068. [_pendingViewState applyToView:self.view withSpecialPropertiesHandling:specialPropertiesHandling];
  3069. }
  3070. // _ASPendingState objects can add up very quickly when adding
  3071. // many nodes. This is especially an issue in large collection views
  3072. // and table views. This needs to be weighed against the cost of
  3073. // reallocing a _ASPendingState. So in range managed nodes we
  3074. // delete the pending state, otherwise we just clear it.
  3075. if (ASHierarchyStateIncludesRangeManaged(_hierarchyState)) {
  3076. _pendingViewState = nil;
  3077. } else {
  3078. [_pendingViewState clearChanges];
  3079. }
  3080. }
  3081. // This method has proved helpful in a few rare scenarios, similar to a category extension on UIView, but assumes knowledge of _ASDisplayView.
  3082. // It's considered private API for now and its use should not be encouraged.
  3083. - (ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy
  3084. {
  3085. ASDisplayNode *supernode = self.supernode;
  3086. while (supernode) {
  3087. if ([supernode isKindOfClass:supernodeClass])
  3088. return supernode;
  3089. supernode = supernode.supernode;
  3090. }
  3091. if (!checkViewHierarchy) {
  3092. return nil;
  3093. }
  3094. UIView *view = self.view.superview;
  3095. while (view) {
  3096. ASDisplayNode *viewNode = ((_ASDisplayView *)view).asyncdisplaykit_node;
  3097. if (viewNode) {
  3098. if ([viewNode isKindOfClass:supernodeClass])
  3099. return viewNode;
  3100. }
  3101. view = view.superview;
  3102. }
  3103. return nil;
  3104. }
  3105. #pragma mark - Performance Measurement
  3106. - (void)setMeasurementOptions:(ASDisplayNodePerformanceMeasurementOptions)measurementOptions
  3107. {
  3108. ASDN::MutexLocker l(__instanceLock__);
  3109. _measurementOptions = measurementOptions;
  3110. }
  3111. - (ASDisplayNodePerformanceMeasurementOptions)measurementOptions
  3112. {
  3113. ASDN::MutexLocker l(__instanceLock__);
  3114. return _measurementOptions;
  3115. }
  3116. - (ASDisplayNodePerformanceMeasurements)performanceMeasurements
  3117. {
  3118. ASDN::MutexLocker l(__instanceLock__);
  3119. ASDisplayNodePerformanceMeasurements measurements = { .layoutSpecNumberOfPasses = -1, .layoutSpecTotalTime = NAN, .layoutComputationNumberOfPasses = -1, .layoutComputationTotalTime = NAN };
  3120. if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) {
  3121. measurements.layoutSpecNumberOfPasses = _layoutSpecNumberOfPasses;
  3122. measurements.layoutSpecTotalTime = _layoutSpecTotalTime;
  3123. }
  3124. if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation) {
  3125. measurements.layoutComputationNumberOfPasses = _layoutComputationNumberOfPasses;
  3126. measurements.layoutComputationTotalTime = _layoutComputationTotalTime;
  3127. }
  3128. return measurements;
  3129. }
  3130. #pragma mark - Debugging (Private)
  3131. #if ASEVENTLOG_ENABLE
  3132. - (ASEventLog *)eventLog
  3133. {
  3134. return _eventLog;
  3135. }
  3136. #endif
  3137. - (NSMutableArray<NSDictionary *> *)propertiesForDescription
  3138. {
  3139. NSMutableArray<NSDictionary *> *result = [NSMutableArray array];
  3140. if (self.debugName.length > 0) {
  3141. [result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName) }];
  3142. }
  3143. return result;
  3144. }
  3145. - (NSMutableArray<NSDictionary *> *)propertiesForDebugDescription
  3146. {
  3147. NSMutableArray<NSDictionary *> *result = [NSMutableArray array];
  3148. if (self.debugName.length > 0) {
  3149. [result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName)}];
  3150. }
  3151. CGRect windowFrame = [self _frameInWindow];
  3152. if (CGRectIsNull(windowFrame) == NO) {
  3153. [result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }];
  3154. }
  3155. // Attempt to find view controller.
  3156. // Note that the convenience method asdk_associatedViewController has an assertion
  3157. // that it's run on main. Since this is a debug method, let's bypass the assertion
  3158. // and run up the chain ourselves.
  3159. if (_view != nil) {
  3160. for (UIResponder *responder in [_view asdk_responderChainEnumerator]) {
  3161. UIViewController *vc = ASDynamicCast(responder, UIViewController);
  3162. if (vc) {
  3163. [result addObject:@{ @"viewController" : ASObjectDescriptionMakeTiny(vc) }];
  3164. break;
  3165. }
  3166. }
  3167. }
  3168. if (_view != nil) {
  3169. [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_view.frame] }];
  3170. } else if (_layer != nil) {
  3171. [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_layer.frame] }];
  3172. } else if (_pendingViewState != nil) {
  3173. [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_pendingViewState.frame] }];
  3174. }
  3175. // Check supernode so that if we are cell node we don't find self.
  3176. ASCellNode *cellNode = ASDisplayNodeFindFirstSupernodeOfClass(self.supernode, [ASCellNode class]);
  3177. if (cellNode != nil) {
  3178. [result addObject:@{ @"cellNode" : ASObjectDescriptionMakeTiny(cellNode) }];
  3179. }
  3180. [result addObject:@{ @"interfaceState" : NSStringFromASInterfaceState(self.interfaceState)} ];
  3181. if (_view != nil) {
  3182. [result addObject:@{ @"view" : ASObjectDescriptionMakeTiny(_view) }];
  3183. } else if (_layer != nil) {
  3184. [result addObject:@{ @"layer" : ASObjectDescriptionMakeTiny(_layer) }];
  3185. } else if (_viewClass != nil) {
  3186. [result addObject:@{ @"viewClass" : _viewClass }];
  3187. } else if (_layerClass != nil) {
  3188. [result addObject:@{ @"layerClass" : _layerClass }];
  3189. } else if (_viewBlock != nil) {
  3190. [result addObject:@{ @"viewBlock" : _viewBlock }];
  3191. } else if (_layerBlock != nil) {
  3192. [result addObject:@{ @"layerBlock" : _layerBlock }];
  3193. }
  3194. return result;
  3195. }
  3196. - (NSString *)description
  3197. {
  3198. return ASObjectDescriptionMake(self, [self propertiesForDescription]);
  3199. }
  3200. - (NSString *)debugDescription
  3201. {
  3202. return ASObjectDescriptionMake(self, [self propertiesForDebugDescription]);
  3203. }
  3204. // This should only be called for debugging. It's not thread safe and it doesn't assert.
  3205. // NOTE: Returns CGRectNull if the node isn't in a hierarchy.
  3206. - (CGRect)_frameInWindow
  3207. {
  3208. if (self.isNodeLoaded == NO || self.isInHierarchy == NO) {
  3209. return CGRectNull;
  3210. }
  3211. if (self.layerBacked) {
  3212. CALayer *rootLayer = _layer;
  3213. CALayer *nextLayer = nil;
  3214. while ((nextLayer = rootLayer.superlayer) != nil) {
  3215. rootLayer = nextLayer;
  3216. }
  3217. return [_layer convertRect:self.threadSafeBounds toLayer:rootLayer];
  3218. } else {
  3219. return [_view convertRect:self.threadSafeBounds toView:nil];
  3220. }
  3221. }
  3222. #pragma mark - NSFastEnumeration
  3223. - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained _Nullable [_Nonnull])buffer count:(NSUInteger)len
  3224. {
  3225. return [self.subnodes countByEnumeratingWithState:state objects:buffer count:len];
  3226. }
  3227. #pragma mark - ASPrimitiveTraitCollection
  3228. - (ASPrimitiveTraitCollection)primitiveTraitCollection
  3229. {
  3230. return _primitiveTraitCollection;
  3231. }
  3232. - (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection
  3233. {
  3234. if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(traitCollection, _primitiveTraitCollection) == NO) {
  3235. _primitiveTraitCollection = traitCollection;
  3236. ASDisplayNodeLogEvent(self, @"asyncTraitCollectionDidChange: %@", NSStringFromASPrimitiveTraitCollection(traitCollection));
  3237. [self asyncTraitCollectionDidChange];
  3238. }
  3239. }
  3240. - (ASTraitCollection *)asyncTraitCollection
  3241. {
  3242. ASDN::MutexLocker l(__instanceLock__);
  3243. return [ASTraitCollection traitCollectionWithASPrimitiveTraitCollection:self.primitiveTraitCollection];
  3244. }
  3245. - (void)asyncTraitCollectionDidChange
  3246. {
  3247. // Subclass override
  3248. }
  3249. ASPrimitiveTraitCollectionDeprecatedImplementation
  3250. #pragma mark - ASLayoutElementStyleExtensibility
  3251. ASLayoutElementStyleExtensibilityForwarding
  3252. #if TARGET_OS_TV
  3253. #pragma mark - UIFocusEnvironment Protocol (tvOS)
  3254. - (void)setNeedsFocusUpdate
  3255. {
  3256. }
  3257. - (void)updateFocusIfNeeded
  3258. {
  3259. }
  3260. - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
  3261. {
  3262. return NO;
  3263. }
  3264. - (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
  3265. {
  3266. }
  3267. - (UIView *)preferredFocusedView
  3268. {
  3269. if (self.nodeLoaded) {
  3270. return self.view;
  3271. } else {
  3272. return nil;
  3273. }
  3274. }
  3275. #endif
  3276. #pragma mark - Deprecated
  3277. // This methods cannot be moved into the category ASDisplayNode (Deprecated). So they need to be declared in ASDisplayNode until removed
  3278. - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
  3279. {
  3280. return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];
  3281. }
  3282. - (BOOL)usesImplicitHierarchyManagement
  3283. {
  3284. return self.automaticallyManagesSubnodes;
  3285. }
  3286. - (void)setUsesImplicitHierarchyManagement:(BOOL)enabled
  3287. {
  3288. self.automaticallyManagesSubnodes = enabled;
  3289. }
  3290. @end
  3291. #pragma mark - ASDisplayNode (Debugging)
  3292. @implementation ASDisplayNode (Debugging)
  3293. - (NSString *)descriptionForRecursiveDescription
  3294. {
  3295. NSString *creationTypeString = nil;
  3296. #if TIME_DISPLAYNODE_OPS
  3297. creationTypeString = [NSString stringWithFormat:@"cr8:%.2lfms dl:%.2lfms ap:%.2lfms ad:%.2lfms", 1000 * _debugTimeToCreateView, 1000 * _debugTimeForDidLoad, 1000 * _debugTimeToApplyPendingState, 1000 * _debugTimeToAddSubnodeViews];
  3298. #endif
  3299. return [NSString stringWithFormat:@"<%@ alpha:%.2f isLayerBacked:%d frame:%@ %@>", self.description, self.alpha, self.isLayerBacked, NSStringFromCGRect(self.frame), creationTypeString];
  3300. }
  3301. - (NSString *)displayNodeRecursiveDescription
  3302. {
  3303. return [self _recursiveDescriptionHelperWithIndent:@""];
  3304. }
  3305. - (NSString *)_recursiveDescriptionHelperWithIndent:(NSString *)indent
  3306. {
  3307. NSMutableString *subtree = [[[indent stringByAppendingString: self.descriptionForRecursiveDescription] stringByAppendingString:@"\n"] mutableCopy];
  3308. for (ASDisplayNode *n in self.subnodes) {
  3309. [subtree appendString:[n _recursiveDescriptionHelperWithIndent:[indent stringByAppendingString:@" | "]]];
  3310. }
  3311. return subtree;
  3312. }
  3313. #pragma mark - ASLayoutElementAsciiArtProtocol
  3314. - (NSString *)asciiArtString
  3315. {
  3316. return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]];
  3317. }
  3318. - (NSString *)asciiArtName
  3319. {
  3320. NSString *string = NSStringFromClass([self class]);
  3321. if (_debugName) {
  3322. string = [string stringByAppendingString:[NSString stringWithFormat:@"\"%@\"",_debugName]];
  3323. }
  3324. return string;
  3325. }
  3326. @end
  3327. #pragma mark - ASDisplayNode UIKit / CA Categories
  3328. // We use associated objects as a last resort if our view is not a _ASDisplayView ie it doesn't have the _node ivar to write to
  3329. static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
  3330. @implementation UIView (ASDisplayNodeInternal)
  3331. - (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
  3332. {
  3333. ASWeakProxy *weakProxy = [ASWeakProxy weakProxyWithTarget:node];
  3334. objc_setAssociatedObject(self, ASDisplayNodeAssociatedNodeKey, weakProxy, OBJC_ASSOCIATION_RETAIN); // Weak reference to avoid cycle, since the node retains the view.
  3335. }
  3336. - (ASDisplayNode *)asyncdisplaykit_node
  3337. {
  3338. ASWeakProxy *weakProxy = objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
  3339. return weakProxy.target;
  3340. }
  3341. @end
  3342. @implementation CALayer (ASDisplayNodeInternal)
  3343. - (void)setAsyncdisplaykit_node:(ASDisplayNode *)node
  3344. {
  3345. ASWeakProxy *weakProxy = [ASWeakProxy weakProxyWithTarget:node];
  3346. objc_setAssociatedObject(self, ASDisplayNodeAssociatedNodeKey, weakProxy, OBJC_ASSOCIATION_RETAIN); // Weak reference to avoid cycle, since the node retains the layer.
  3347. }
  3348. - (ASDisplayNode *)asyncdisplaykit_node
  3349. {
  3350. ASWeakProxy *weakProxy = objc_getAssociatedObject(self, ASDisplayNodeAssociatedNodeKey);
  3351. return weakProxy.target;
  3352. }
  3353. @end
  3354. @implementation UIView (AsyncDisplayKit)
  3355. - (void)addSubnode:(ASDisplayNode *)subnode
  3356. {
  3357. if (subnode.layerBacked) {
  3358. // Call -addSubnode: so that we use the asyncdisplaykit_node path if possible.
  3359. [self.layer addSubnode:subnode];
  3360. } else {
  3361. ASDisplayNode *selfNode = self.asyncdisplaykit_node;
  3362. if (selfNode) {
  3363. [selfNode addSubnode:subnode];
  3364. } else {
  3365. if (subnode.supernode) {
  3366. [subnode removeFromSupernode];
  3367. }
  3368. [self addSubview:subnode.view];
  3369. }
  3370. }
  3371. }
  3372. @end
  3373. @implementation CALayer (AsyncDisplayKit)
  3374. - (void)addSubnode:(ASDisplayNode *)subnode
  3375. {
  3376. ASDisplayNode *selfNode = self.asyncdisplaykit_node;
  3377. if (selfNode) {
  3378. [selfNode addSubnode:subnode];
  3379. } else {
  3380. if (subnode.supernode) {
  3381. [subnode removeFromSupernode];
  3382. }
  3383. [self addSublayer:subnode.layer];
  3384. }
  3385. }
  3386. @end
  3387. #pragma mark - ASDisplayNode (Deprecated)
  3388. @implementation ASDisplayNode (Deprecated)
  3389. - (NSString *)name
  3390. {
  3391. return self.debugName;
  3392. }
  3393. - (void)setName:(NSString *)name
  3394. {
  3395. self.debugName = name;
  3396. }
  3397. - (void)setPreferredFrameSize:(CGSize)preferredFrameSize
  3398. {
  3399. // Deprecated preferredFrameSize just calls through to set width and height
  3400. self.style.preferredSize = preferredFrameSize;
  3401. [self setNeedsLayout];
  3402. }
  3403. - (CGSize)preferredFrameSize
  3404. {
  3405. ASLayoutSize size = self.style.preferredLayoutSize;
  3406. BOOL isPoints = (size.width.unit == ASDimensionUnitPoints && size.height.unit == ASDimensionUnitPoints);
  3407. return isPoints ? CGSizeMake(size.width.value, size.height.value) : CGSizeZero;
  3408. }
  3409. - (CGSize)measure:(CGSize)constrainedSize
  3410. {
  3411. return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
  3412. }
  3413. ASLayoutElementStyleForwarding
  3414. - (void)visibilityDidChange:(BOOL)isVisible
  3415. {
  3416. if (isVisible) {
  3417. [self didEnterVisibleState];
  3418. } else {
  3419. [self didExitVisibleState];
  3420. }
  3421. }
  3422. - (void)visibleStateDidChange:(BOOL)isVisible
  3423. {
  3424. if (isVisible) {
  3425. [self didEnterVisibleState];
  3426. } else {
  3427. [self didExitVisibleState];
  3428. }
  3429. }
  3430. - (void)displayStateDidChange:(BOOL)inDisplayState
  3431. {
  3432. if (inDisplayState) {
  3433. [self didEnterVisibleState];
  3434. } else {
  3435. [self didExitVisibleState];
  3436. }
  3437. }
  3438. - (void)loadStateDidChange:(BOOL)inLoadState
  3439. {
  3440. if (inLoadState) {
  3441. [self didEnterPreloadState];
  3442. } else {
  3443. [self didExitPreloadState];
  3444. }
  3445. }
  3446. - (void)fetchData
  3447. {
  3448. // subclass override
  3449. }
  3450. - (void)clearFetchedData
  3451. {
  3452. // subclass override
  3453. }
  3454. - (void)cancelLayoutTransitionsInProgress
  3455. {
  3456. [self cancelLayoutTransition];
  3457. }
  3458. @end