in RenderCore/View/ComponentViewManager.mm [246:334]
void AttributeApplicator::applyAttributes(UIView *view, std::shared_ptr<const CKViewComponentAttributeValueMap> attributes) noexcept
{
CK::Component::ActionDisabler actionDisabler; // We never want implicit animations when applying attributes
// Avoid the static destructor fiasco, use a pointer:
static const auto *empty = new CKViewComponentAttributeValueMap();
CKComponentAttributeSetWrapper *const wrapper = attributeSetWrapperForView(view);
const bool useNewStyleOptimisticMutations = CKReadGlobalConfig().useNewStyleOptimisticMutations;
const bool hasNewStyleOptimisticViewMutations = useNewStyleOptimisticMutations && !wrapper->_optimisticViewMutations.empty();
if (!useNewStyleOptimisticMutations) {
// Reset optimistic mutations so that applicators see they see the state they expect.
if (!wrapper->_optimisticViewMutationTeardowns_Old.empty()) {
const auto copiedTeardowns = wrapper->_optimisticViewMutationTeardowns_Old;
wrapper->_optimisticViewMutationTeardowns_Old.clear();
for (CKOptimisticViewMutationTeardown teardown : copiedTeardowns) {
if (teardown) {
teardown(view);
}
}
}
} else {
for (auto it = wrapper->_optimisticViewMutations.rbegin(); it != wrapper->_optimisticViewMutations.rend(); ++it) {
if (it->undo) {
it->undo(view);
}
}
}
const CKViewComponentAttributeValueMap &oldAttributes = wrapper->_attributes ? *wrapper->_attributes : *empty;
const CKViewComponentAttributeValueMap &newAttributes = *attributes;
// First, tear down any attributes that appear in the *old* set but not the new set, and *do* have an unapplicator.
for (const auto &oldAttr : oldAttributes) {
if (oldAttr.first.unapplicator) {
const auto &newAttr = newAttributes.find(oldAttr.first);
if (newAttr == newAttributes.end()) {
// There is no new attribute, so we always must call "unapplicator".
oldAttr.first.unapplicator(view, oldAttr.second);
} else if (!RCObjectIsEqual(newAttr->second, oldAttr.second)) {
// If the attribute has an updater, don't call the unapplicator; instead, the updater will be called below.
if (newAttr->first.updater == nil) {
oldAttr.first.unapplicator(view, oldAttr.second);
}
}
}
}
// Now apply the applicators for all attributes in the *new* set, except those that haven't changed in value.
for (const auto &newAttr : newAttributes) {
const auto &oldAttr = oldAttributes.find(newAttr.first);
if (oldAttr == oldAttributes.end()) {
// There is no old attribute, so we always must call "applicator".
newAttr.first.applicator(view, newAttr.second);
} else if (!RCObjectIsEqual(oldAttr->second, newAttr.second)) {
// If the attribute has an "updater", call that. Otherwise, call the applicator.
if (newAttr.first.updater) {
newAttr.first.updater(view, oldAttr->second, newAttr.second);
} else {
newAttr.first.applicator(view, newAttr.second);
}
}
}
if (hasNewStyleOptimisticViewMutations) {
auto copiedOptimisticMutations = wrapper->_optimisticViewMutations;
wrapper->_suppressApply = true;
for (auto optimisticMutation : copiedOptimisticMutations) {
optimisticMutation.load(view);
}
wrapper->_suppressApply = false;
if (copiedOptimisticMutations.size() != wrapper->_optimisticViewMutations.size()) {
copiedOptimisticMutations = wrapper->_optimisticViewMutations;
}
for (auto optimisticMutation : copiedOptimisticMutations) {
optimisticMutation.apply(view);
}
}
// Update the wrapper to reference the new attributes. Don't do this before now since it changes oldAttributes.
wrapper->_attributes = std::move(attributes);
}