RenderCore/CKMountableHelpers.mm (103 lines of code) (raw):

/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #import "CKMountableHelpers.h" #import <RenderCore/RCAssert.h> #import <RenderCore/RCComponentDescriptionHelper.h> #import <RenderCore/CKMountable.h> #import <RenderCore/CKMountableHelpers.h> #import <RenderCore/CKMountedObjectForView.h> #import <RenderCore/CKViewConfiguration.h> #import <RenderCore/RCFatal.h> static void relinquishMountedView(std::unique_ptr<CKMountInfo> &mountInfo, id<CKMountable> mountable, CKMountCallbackFunction willRelinquishViewFunction) { RCCAssertMainThread(); RCCAssert(mountInfo, @"mountInfo should not be null"); if (mountInfo) { UIView *view = mountInfo->view; if (view) { if (willRelinquishViewFunction) { willRelinquishViewFunction(mountable, view); } RCCAssert(CKMountedObjectForView(view) == mountable, @""); CKSetMountedObjectForView(view, nil); mountInfo->view = nil; } } } CK::Component::MountResult CKPerformMount(std::unique_ptr<CKMountInfo> &mountInfo, const RCLayout &layout, const CKViewConfiguration &viewConfiguration, const CK::Component::MountContext &context, const id<CKMountable> supercomponent, const CKMountCallbackFunction didAcquireViewFunction, const CKMountCallbackFunction willRelinquishViewFunction, const CKMountAnimationBlockCallbackFunction blockAnimationIfNeededFunction, const CKMountAnimationUnblockCallbackFunction unblockAnimationFunction) { RCCAssertMainThread(); if (!mountInfo) { mountInfo.reset(new CKMountInfo()); } mountInfo->supercomponent = supercomponent; UIView *v = context.viewManager->viewForConfiguration(layout.component.class, viewConfiguration); if (v) { auto const currentMountedComponent = (id<CKMountable>)CKMountedObjectForView(v); BOOL didBlockAnimation = NO; if (blockAnimationIfNeededFunction) { didBlockAnimation = blockAnimationIfNeededFunction(currentMountedComponent, layout.component, context, viewConfiguration); } // Mount a view for the component if needed. UIView *acquiredView = nil; if (mountInfo->view != v) { relinquishMountedView(mountInfo, layout.component, willRelinquishViewFunction); // First release our old view [currentMountedComponent unmount]; // Then unmount old component (if any) from the new view CKSetMountedObjectForView(v, layout.component); CK::Component::AttributeApplicator::apply(v, viewConfiguration); acquiredView = v; mountInfo->view = v; } else { RCCAssert(currentMountedComponent == layout.component, @""); } @try { CKSetViewPositionAndBounds(v, context, layout.size); } @catch (NSException *exception) { NSString *const componentBacktraceDescription = RCComponentBacktraceDescription(RCComponentGenerateBacktrace(supercomponent)); NSString *const componentChildrenDescription = RCComponentChildrenDescription(layout.children); RCCFatalWithCategory(exception.name, @"%@ raised %@ during mount: %@\n backtrace:%@ children:%@", layout.component.class, exception.name, exception.reason, componentBacktraceDescription, componentChildrenDescription); } mountInfo->viewContext = {v, {{0,0}, v.bounds.size}}; // If the mount process acquired a new view, trigger the didAcquireView callback. if (acquiredView && didAcquireViewFunction) { didAcquireViewFunction(layout.component, acquiredView); } if (didBlockAnimation && unblockAnimationFunction) { unblockAnimationFunction(); } return {.mountChildren = YES, .contextForChildren = context.childContextForSubview(v, didBlockAnimation)}; } else { RCCAssertWithCategory(mountInfo->view == nil, layout.component.class, @"%@ should not have a mounted %@ after previously being mounted without a view.\n%@", layout.component.class, [mountInfo->view class], RCComponentBacktraceDescription(RCComponentGenerateBacktrace(layout.component))); mountInfo->viewContext = {context.viewManager->view, {context.position, layout.size}}; return {.mountChildren = YES, .contextForChildren = context}; } } void CKPerformUnmount(std::unique_ptr<CKMountInfo> &mountInfo, const id<CKMountable> mountable, const CKMountCallbackFunction willRelinquishViewFunction) { RCCAssertMainThread(); if (mountInfo) { relinquishMountedView(mountInfo, mountable, willRelinquishViewFunction); mountInfo.reset(); } } void CKSetViewPositionAndBounds(UIView *v, const CK::Component::MountContext &context, const CGSize size) { const CGPoint anchorPoint = v.layer.anchorPoint; [v setCenter:context.position + CGPoint({size.width * anchorPoint.x, size.height * anchorPoint.y})]; [v setBounds:{v.bounds.origin, size}]; }