The introduction

UILabel is a highly visible UI control in daily development. This article will introduce how Apple designed the text information management of UILabel, and what pitfalls we will encounter in daily use.

Basic properties

UILabel *label = [[UILabel alloc] init];
NSLog(@ "% @", label.textColor);
NSLog(@ "% @", label.font);
NSLog(@ "% @", label.shadowColor);
NSLog(@ "% @", @(label.shadowOffset));

// How are these attributes stored?
<UIDynamicSystemColor: 0x6000031597c0; name = labelColor>
<UICTFont: 0x7ffdb9e08fb0> font-family: ".SFUI-Regular"; font-weight: normal; font-style: normal; font-size: 17.00pt
(null)
NSSize: {0.- 1}
Copy the code

Hypothesis 1

Stored by the corresponding member variables of these attributes.

By printing the list of member variables of UILabel object, we find that there are no corresponding _textColor, _font, _shadowColor, _shadowOffset member variables. So where are these attributes stored?

After a closer look at the list, we find an interesting member variable: _content.

_content (_UILabelContent*): <_UILabelContent: 0x60000260cac0>

in UILabel:
	_size (struct CGSize) : {0.0}
	_highlightedColor (UIColor*) :nil
	_numberOfLines (long) :1
	_baselineInfo (struct?). : { firstBaseline (double) :0
		lastBaseline (double) :0
		referenceBounds (struct CGRect) : {{0.0}, {0.0}}
		measuredNumberOfLines (long) :- 1
	}
	_previousBaselineOffsetFromBottom (double) :0
	_previousFirstLineBaseline (double) :0
	_minimumScaleFactor (double) :0
	_content (_UILabelContent*): <_UILabelContent: 0x60000260cac0>
	_synthesizedAttributedText (NSAttributedString*) :nil
	_cachedSynthesizedTextAttributes (NSDictionary*) :nil
	_fallbackColorsForUserInterfaceStyle (NSMutableDictionary*) :nil
	_minimumFontSize (double) :0
	_lineSpacing (long) :0
	_layout (id) :nil
	_scaledMetrics (_UILabelScaledMetrics*): nil
	_intrinsicContentSizeCache (_UITextSizeCache*): nil
	_contentsFormat (long) :0
	_cuiCatalog (CUICatalog*): nil
	_cuiStyleEffectConfiguration (CUIStyleEffectConfiguration*): <CUIStyleEffectConfiguration: 0x600000701f40>
	_marqueeAnimations (NSMutableDictionary*) :nil
	_marqueeMaskAnimations (NSMutableDictionary*) :nil
	_textLabelFlags (struct?). : { highlighted (b1):NO
		autosizeTextToFit (b1): NO
		supportMultiLineShrinkToFit (b1): NO
		autotrackTextToFit (b1): NO
		baselineAdjustment (b2): 0
		enabled (b1): YES
		wordRoundingEnabled (b1): NO
		explicitAlignment (b1): NO
		enablesMarqueeWhenAncestorFocused (b1): NO
		marqueeEnabled (b1): NO
		marqueeRunable (b1): NO
		marqueeRequired (b1): NO
		usesExplicitPreferredMaxLayoutWidth (b1): NO
		drawsDebugBaselines (b1): NO
		explicitBaselineOffset (b1): NO
		usesSimpleTextEffects (b1): NO
		wantsUnderlineForAccessibilityButtonShapesEnabled (b1): NO
		disableUpdateTextColorOnTraitCollectionChange (b1): NO
		textAlignmentFollowsWritingDirection (b1): NO
		textAlignmentMirrored (b1): NO
		shortcutIntrinsicContentSize (b1): NO
		multilineLabelRequiresCarefulMeasurement (b1): NO
		noNeedsDisplayCheckForBaselineCalculationNeeded (b1): NO
		overallWritingDirectionFollowsLayoutDirection (b1): NO
		hyphenationFactorIgnoredIfURLsDetected (b1): NO
		extendedAccessibilityAdjustments (b1): YES
		canUseUILabelLayer (b1): YES
		implementsDefaultAttributes (b1): NO
		textColorFollowsTintColor (b1): NO
	}
	_adjustsFontForContentSizeCategory (BOOL) :NO
	_preferredMaxLayoutWidth (double) :0
	_multilineContextWidth (double) :0
	_fontForShortcutBaselineCalculation (UIFont*) :nil
	__visualStyle (_UILabelVisualStyle*): <_UILabelVisualStyle_iOS: 0x60000260c1f0>
in UIView:
	_constraintsExceptingSubviewAutoresizingConstraints (NSMutableArray*) :nil
	_cachedTraitCollection (UITraitCollection*).UITraitCollection: 0x600001f14b60>
	_animationInfo (UIViewAnimationInfo*) :nil
	_layer (CALayer*): <_UILabelLayer: 0x600000701ef0>
	_layerRetained (CALayer*): <_UILabelLayer: 0x600000701ef0>
	_gestureRecognizers (NSMutableArray*) :nil
	_window (UIWindow*) :nil
	_subviewCache (NSArray*) :nil
	_viewDelegate (UIViewController*) :nil
	_cachedScreenScale (double) :3
	_layoutEngineWidth (double) :0
	_viewFlags (struct?). : { userInteractionDisabled (b1):YES
		implementsDrawRect (b1): YES
		implementsDidScroll (b1): NO
		implementsMouseTracking (b1): NO
		implementsIntrinsicContentSize (b1): YES
		hasBackgroundColor (b1): YES
		hasBackgroundColorSystemColorName (b1): YES
		hasInteractionTintColor (b1): NO
		isOpaque (b1): YES
		becomeFirstResponderWhenCapable (b1): NO
		interceptMouseEvent (b1): NO
		deallocating (b1): NO
		isInUIViewDealloc (b1): NO
		debugFlash (b1): NO
		isAncestorOfFirstResponder (b1): NO
		dontAutoresizeSubviews (b1): NO
		autoresizeMask (b6): 0
		patternBackground (b1): NO
		fixedBackgroundPattern (b1): NO
		dontAnimate (b1): NO
		superLayerIsView (b1): NO
		layerKitPatternDrawing (b1): NO
		multipleTouchEnabled (b1): NO
		exclusiveTouch (b1): NO
		hasViewController (b1): NO
		needsDidAppearOrDisappear (b1): NO
		deliversTouchesForGesturesToSuperview (b1): YES
		deliversButtonsForGesturesToSuperview (b1): YES
		chargeEnabled (b1): NO
		skipsSubviewEnumeration (b1): NO
		needsDisplayOnBoundsChange (b1): NO
		hasTiledLayer (b1): NO
		hasLargeContent (b1): NO
		traversalMark (b1): NO
		appearanceIsInvalid (b1): NO
		monitorsSubtree (b1): NO
		hostsAutolayoutEngine (b1): NO
		constraintsAreClean (b1): NO
		subviewLayoutConstraintsAreClean (b1): NO
		intrinsicContentSizeConstraintsAreClean (b1): NO
		strictDescendantNeedsDoubleUpdateConstraints (b1): NO
		strictDescendantNeedsDoubleUpdateConstraintsIsInvalid (b1): NO
		determiningWidthForDoubleUpdateConstraints (b1): NO
		inSecondConstraintsPass (b1): NO
		potentiallyHasDanglyConstraints (b1): NO
		doesNotTranslateAutoresizingMaskIntoConstraints (b1): NO
		autolayoutIsClean (b1): NO
		autolayoutBoundsAreClean (b1): NO
		layoutFlushingDisabled (b1): NO
		layingOutFromConstraints (b1): NO
		wantsAutolayout (b1): NO
		subviewWantsAutolayout (b1): NO
		isApplyingValuesFromEngine (b1): NO
		isResizingDueToParentResize (b1): NO
		isInLayoutSubviewsOrVCCallback (b1): NO
		isInAnimatedLayout (b1): NO
		isSubviewUpdatingAutoresizingConstraints (b1): NO
		isUpdatingConstraints (b1): NO
		isReapplyingStillActiveBrokenConstraints (b1): NO
		isHostingUpdateConstraintsPassDuringLayout (b1): NO
		isRunningEngineLevelConstraintsPass (b1): NO
		isUpdatingLayoutEngineHostConstraints (b1): NO
		isExpectingToFlushPendingLayoutChangeNotifications (b1): NO
		systemLayoutFittingSizeNeedsUpdate (b1): NO
		systemLayoutFittingSizeNeedsUpdateInWholeSubtree (b1): NO
		isCalculatingSystemLayoutFittingSize (b1): NO
		suppressEncapsulationConstraints (b1): NO
		isFetchingSizeForTAMIC_NOEngineHost (b1): NO
		stayHiddenAwaitingReuse (b1): NO
		stayHiddenAfterReuse (b1): NO
		skippedLayoutWhileHiddenForReuse (b1): NO
		isPendingHiddenForAnimation (b1): NO
		hasMaskView (b1): NO
		hasVisualAltitude (b1): NO
		hasBackdropMaskViews (b1): NO
		backdropMaskViewFlags (b5): 0
		delaysTouchesForSystemGestures (b1): NO
		subclassShouldDelayTouchForSystemGestures (b1): NO
		hasMotionEffects (b1): NO
		backdropOverlayMode (b2): 0
		tintAdjustmentMode (b2): 0
		isReferenceView (b1): NO
		focusState (b2): 0
		hasUserInterfaceIdiom (b1): NO
		userInterfaceIdiom (b3): 0
		ancestorDefinesTintColor (b1): NO
		ancestorDefinesTintAdjustmentMode (b1): NO
		ancestorIgnoresInvertColors (b1): NO
		needsTraitCollectionDidChangePropagation (b1): NO
		isRootOfTraitCollectionDidChangePropagation (b1): NO
		implementsTraitCollectionForChildEnvironment (b1): NO
		implementsBaselineOffsetsAtSize (b1): YES
		coloredViewBounds (b1): NO
		coloredAlignmentRects (b1): NO
		preservesSuperviewMargins (b4): 0
		insettingLayoutMarginsFromSafeArea (b4): 15
		safeAreaInsetsFrozen (b1): NO
		viewDelegateContentOverlayInsetsAreClean (b1): NO
		hasGeometryObservers (b1): NO
		observingGeometryChangesForSelfCount (b4): 0
		hasTraitStorageList (b1): NO
		cachedTraitCollectionIsValid (b1): YES
		dontUpdateInferredLayoutMargins (b1): NO
		areLayoutMarginsDirectional (b1): NO
		implementsViewForBaselineLayout (b1): NO
		tracksFocusedAncestors (b1): NO
		hasLayoutArrangements (b1): NO
		isHiddenManagedByLayoutArrangement (b1): NO
		hasAddedFocusGuides (b1): NO
		hasFocusSpeedBumpEdges (b1): NO
		hasFocusableContentMargins (b1): NO
		focusInteractionDisabled (b1): NO
		shouldReverseLayoutDirection (b1): NO
		cannotBeParentTraitEnvironment (b1): NO
		hasTemplateLayoutView (b2): 0
		ignoresTemplateLayoutView (b2): 0
		needsContentsFormatUpdate (b1): YES
		accessibilityIgnoresInvertColors (b1): NO
		ignoresLayerTransformForSafeAreaInsets (b1): NO
		accessibilityInterfaceStyleIntent (b2): 0
		accessibilityResolvedInterfaceStyle (b2): 0
		shouldArchiveUIAppearanceTags (b1): NO
		wantsDeepColorDrawing (b1): YES
		tagEnabled (b1): NO
		chargeSet (b1): NO
		ignoreBackdropViewsWhenHiding (b1): NO
		areChildrenFocused (b1): NO
		hasInteractionsArray (b1): NO
		hasHitTestDirectionalInsets (b1): NO
		hasLayoutDebuggingIdentifier (b1): NO
		hasContentSizeNotificationToken (b1): NO
		hasPresentationControllerToNotifyOnLayoutSubviews (b1): NO
		semanticContentAttribute (b3): 0
		hasDynamicBackgroundColor (b1): NO
		hasLocalOverrideTraitCollection (b1): NO
		forceEffectiveThemeDidChange (b1): NO
		allowsHighContrastForBackgroundColor (b1): NO
		hasPendingTraitStorageConstraints (b1): NO
		hasEverBeenInAWindow (b1): NO
		hasFocusGroupIdentifier (b2): 0
		allowsSkippingLayout (b1): YES
	}
	_unsatisfiableConstraintsLoggingSuspensionCount (unsigned short): Value not representable, S
	_pseudo_id (unsigned int) :35
	_retainCount (long) :0
	_draggingSourceDelegate (<_UIViewInternalDraggingSourceDelegate>*): nil
	_tintAdjustmentDimmingCount (unsigned short): Value not representable, S
	_layoutSubviewsCount (unsigned short): Value not representable, S
	_imminentLayoutSubviewsCount (unsigned short): Value not representable, S
	_countOfFocusedAncestorTrackingViewsInSubtree (unsigned short): Value not representable, S
	_layoutMarginsGuide (UILayoutGuide*) :nil
	_minXVariable (NSISVariable*) :nil
	_minYVariable (NSISVariable*) :nil
	_boundsWidthVariable (NSISVariable*) :nil
	_boundsHeightVariable (NSISVariable*) :nil
	_layoutEngine (NSISEngine*) :nil
	_stashedLayoutVariableObservations (NSMapTable*) :nil
	_internalConstraints (NSMutableArray*) :nil
	_safeAreaLayoutGuide (UILayoutGuide*) :nil
	_readableContentGuide (UILayoutGuide*) :nil
	__preferedContentsFormat (long) :0
	__lastNotifiedTraitCollection (UITraitCollection*).UITraitCollection: 0x600001f14b60>
	__alignmentRectOriginCache (_UIViewLayoutEngineRelativeAlignmentRectOriginCache*): nil
	_rawLayoutMargins (struct UIEdgeInsets) : {1.7976931348623157 e+308.1.7976931348623157 e+308.1.7976931348623157 e+308.1.7976931348623157 e+308}
	_inferredLayoutMargins (struct UIEdgeInsets) : {1.7976931348623157 e+308.1.7976931348623157 e+308.1.7976931348623157 e+308.1.7976931348623157 e+308}
	_safeAreaInsets (struct UIEdgeInsets) : {0.0.0.0}
in UIResponder:
	_responderFlags (struct?). : { hasOverrideClient (b1):NO
		hasOverrideHost (b1): NO
		hasInputAssistantItem (b1): NO
		suppressSoftwareKeyboard (b1): NO
	}
in NSObject:
	isa (Class): UILabel (isa, 0x7fff86d7b1c0)
Copy the code

Suppose 2

Stored via the member variable: _content.

(lldb) po [label valueForKey:@"content"]
<_UILabelContent:0x60000260cac0 attr={
    NSColor = "<UIDynamicSystemColor: 0x6000031597c0; name = labelColor>";
    NSFont = "
      
        font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; The font - size: 17.00 pt"
      ;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}"; } >Copy the code

We print the _content value via KVC and see that these attributes are indeed stored in the _content member variable.

NSLog(@”%@”, label.textColor);

<UIDynamicSystemColor: 0x6000031597c0; name = labelColor>

NSColor and label.textColor refer to the same object in _content.

NSColor = “<UIDynamicSystemColor: 0x6000031597c0; name = labelColor>”;

In addition, other attributes of UILabel such as textAlignment and lineBreakMode are also stored in the NSParagraphStyle of _content.

validation

We can also verify that our guess is correct by assigning values to these attributes and looking at the value of _content.

- (void)viewDidLoad {
    [super viewDidLoad];
    UILabel *label = [[UILabel alloc] init];
    label.textColor = [UIColor redColor];
    label.font = [UIFont systemFontOfSize:40.0];
    label.shadowColor = [UIColor redColor];
    label.shadowOffset = CGSizeZero;
    NSLog(@ "% @", label.textColor);
    NSLog(@ "% @", label.font);
    NSLog(@ "% @", label.shadowColor);
    NSLog(@ "% @", @(label.shadowOffset));
    NSLog(@ "% @", [label valueForKey:@"content"]);
}

/ / output
UIExtendedSRGBColorSpace 1 0 0 1
<UICTFont: 0x7fea0e8096c0> font-family: ".SFUI-Regular"; font-weight: normal; font-style: normal; font-size: 40.00pt
UIExtendedSRGBColorSpace 1 0 0 1
NSSize: {0.0}
<_UILabelContent:0x6000025fc6c0 attr={
    NSColor = "UIExtendedSRGBColorSpace 1 0 0 1";
    NSFont = "
      
        font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; The font - size: 40.00 pt"
      ;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, 0} color = {UIExtendedSRGBColorSpace 1 0 0 1}"; } >Copy the code

The text property

So we see that the _content of UILabel after initialization stores the _UILabelContent object. What happens if we assign through text?

- (void)viewDidLoad {
    [super viewDidLoad];
    UILabel *label = [[UILabel alloc] init];
    label.text = @"ByteDance";
}

(lldb) po [label valueForKey:@"content"]
<_UILabelStringContent:0x600003573780 len=9 ByteDance attr={
    NSColor = "<UIDynamicSystemColor: 0x60000206a980; name = labelColor>";
    NSFont = "
      
        font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; The font - size: 17.00 pt"
      ;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}"; } >Copy the code

We see that _content stores the _UILabelStringContent object, which records our string @”ByteDance”, length 9, and property information.

In other words, the string that we put in text ends up being rich text by UILabel.

AttributedText properties

The text property is relatively simple, with few potholes. But attributedText is a lot more complicated, and if you mix it with text, sometimes you set text, sometimes you set attributedText, it’s even more complicated.

Question 1

How does UILabel display when we don’t add any attributes to the attributedText?

UILabel *label = [[UILabel alloc] init];
NSString *text = @"ByteDance";
label.attributedText = [[NSAttributedString alloc] initWithString:text];
Copy the code

Let’s look at the _content property.

UILabel *label = [[UILabel alloc] init];
NSString *text = @"ByteDance";
label.attributedText = [[NSAttributedString alloc] initWithString:text];
NSLog(@ "% @", [label valueForKey:@"content"]);

/ / output
<_UILabelAttributedStringContent:0x600003b88840 attributed len=9 ByteDance attr={
    NSColor = "<UIDynamicSystemColor: 0x600002ed7900; name = labelColor>";
    NSFont = "
      
        font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; The font - size: 17.00 pt"
      ;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}"; } >Copy the code

You can see the _content properties, storage is _UILabelAttributedStringContent object, including a rich text, length of 9 and attribute information, attribute information are the default values.

So what does rich text store?

(lldb) po [0x600003b88840 valueForKey:@"attributedString"]
ByteDance{
}
Copy the code

When we print it out, it’s just a string, no attribute information.

That is, when UILabel presents, it adds default attributes to strings that have no attributes.

Question 2

What happens if we just add attributes to the attributedText part?

UILabel *label = [[UILabel alloc] init];
NSString *text = @"ByteDance";
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0.1)];
label.attributedText = attStr;

id content = [label valueForKey:@"content"];
NSLog(@ "% @", content);

id attributedString = [content valueForKey:@"attributedString"];
NSLog(@ "% @", attributedString);

/ / output
<_UILabelAttributedStringContent:0x600000048200 attributed len=9 ByteDance attr={
    NSColor = "<UIDynamicSystemColor: 0x600001522500; name = labelColor>";
    NSFont = "
      
        font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; The font - size: 17.00 pt"
      ;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}";
}>

B{
    NSColor = "UIExtendedSRGBColorSpace 1 0 0 1";
}yteDance{
}
Copy the code

We can see that the part with added attributes uses the added attributes, while the part without added attributes still uses the default attributes.

In other words, there are two factors that determine the final display:

  • The attribute of the attributedText itself (highest priority)
  • If no properties are set, properties in UILabel are used

Pit point 1

Again, what is the color of label.textColor?

UILabel *label = [[UILabel alloc] init];
NSString *text = @"ByteDance";
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0.1)];
label.attributedText = attStr;
NSLog(@ "% @", label.textColor);
Copy the code

Answer: redColor

UIExtendedSRGBColorSpace 1 0 0 1

So if YOU change range to NSMakeRange(1, 1) and ask: What is the color of label.textColor?

UILabel *label = [[UILabel alloc] init];
NSString *text = @"ByteDance";
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(1.1)];
label.attributedText = attStr;
NSLog(@ "% @", label.textColor);
Copy the code

Answer: Default color

<UIDynamicSystemColor: 0x600002dc1ec0; name = labelColor>

To explore the

Again, let’s take the textColor property as an example. We found system call defaultValueForAttribute: view the source code to get the value, if there is a value is returned the value; Otherwise, _defaultColor is returned.

We further defaultValueForAttribute:. We find that if the length of the text is greater than or equal to one, and the 0th bit of the attributedString is configured, it returns that value; Otherwise, the relevant configuration values are taken from the ATTR dictionary.

This also explains how UILabel takes text attributes for rendering.

conclusion

In daily use, we need to pay attention to the usage scenario when using UILabel configuration attributes, especially after assigning the value to the attributedText attribute, the attribute acquisition will be different, which requires special attention.

Question 3

Question 2: If we only add attributes to the attributedText part, what happens if we add attributes to the whole thing?

Look _UILabelAttributedStringContent stored in content and in question 2, only the rich text attributes are added to the whole.

UILabel *label = [[UILabel alloc] init];
NSString *text = @"ByteDance";
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, attStr.length)];
label.attributedText = attStr;

id content = [label valueForKey:@"content"];
NSLog(@ "% @", content);

id attributedString = [content valueForKey:@"attributedString"];
NSLog(@ "% @", attributedString);

/ / output
<_UILabelAttributedStringContent:0x600000da26e0 attributed len=9 ByteDance attr={
    NSColor = "<UIDynamicSystemColor: 0x6000018cc840; name = labelColor>";
    NSFont = "
      
        font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; The font - size: 17.00 pt"
      ;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}";
}>
ByteDance{
    NSColor = "UIExtendedSRGBColorSpace 1 0 0 1";
}
Copy the code

Pit point 2

What happens if we assign a value to text after assigning an attribute to attributedText?

We first experiment with just adding attributes to the attributedText part.

UILabel *label = [[UILabel alloc] init];
NSString *text = @"ByteDance";
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0.1)];
label.attributedText = attStr;
label.text = text;
Copy the code
  • Option 1

  • Option 2

I don’t think you have any questions. The answer is choice 1.

Experiment 2

If you add attributes to the whole attributedText, now what do you choose?

[attStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, attStr.length)];
Copy the code

And the answer is choice 2.

Not only that, but printing _content also finds that the default attr property has changed to redColor.

That is, instead of just adding some attributes to the attributedText, the default color value of the UILabel object has been replaced.

id content = [label valueForKey:@"content"];
NSLog(@ "% @", content);

/ / output
<_UILabelStringContent:0x60000378dfe0 len=9 ByteDance attr={
    NSColor = "UIExtendedSRGBColorSpace 1 0 0 1";
    NSFont = "
      
        font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; The font - size: 17.00 pt"
      ;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}"; } >Copy the code
Experiment 3

Before assigning a value to the text, clear the attributedText. Now what’s your choice?

UILabel *label = [[UILabel alloc] init];
NSString *text = @"ByteDance";
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];
[attStr addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, attStr.length)];
label.attributedText = attStr;
label.attributedText = nil;
label.text = text;
Copy the code

And the answer is choice 1. Printing _content also shows that the default attr property has not changed.

<_UILabelStringContent:0x600001c4fd20 len=9 ByteDance attr={
    NSColor = "<UIDynamicSystemColor: 0x6000009710c0; name = labelColor>";
    NSFont = "
      
        font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; The font - size: 17.00 pt"
      ;
    NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535";
    NSShadow = "NSShadow {0, -1} color = {(null)}"; } >Copy the code
To explore the

From the above experiment, we found that the problem was related to setting the text property.

Through the NSAttributedString attributesAtIndex: longestEffectiveRange: inRange: If longestEffectiveRange and length are the same length and there are values in the dictionary, then we do a series of operations and compare the new dictionary if it is different from attr. And when we finally initialize, we’re going to use this dictionary to initialize the entire _content.

So, when you set an attribute for the entire rich text, you replace the default attribute of the UILabel object.

conclusion

Before assigning a value to the text attribute, clear the value in the attributedText attribute to avoid the possibility of changing the default color value of the UILabel object.


If you found this article helpful, give me a thumbs up ~ 👍🏻