Alpha Value of UIButton set in a storyboard is reset on Light/Dark Mode Transition

Hello fellow developers,

I've encountered a peculiar issue in iOS 16 that I wanted to bring to light and see if anyone else has experienced the same.

Issue Description: When designing a user interface in a storyboard, if I set the alpha value of a UI component (e.g., UIView, UIButton) to 0.0 within the storyboard, and then programmatically change that alpha value to 1.0 during runtime, the alpha value unexpectedly reverts back to its original storyboard value (0.0) when the device transitions between light and dark modes.

Steps to Reproduce:

Create a UI component in a storyboard and set its alpha to 0.0. Programmatically change the alpha value of the component to 1.0 during runtime. Trigger a light/dark mode transition on the device. Observe that the component's alpha value reverts to 0.0. Additional Observations:

This behavior seems as if the UIView is being reloaded from the storyboard during the mode transition. However, breakpoints set in viewDidLoad() and loadView() are not triggered during the mode change, indicating that the views are not being reloaded in the traditional sense. This issue was observed in iOS 16, but it's unclear if it's present in previous iOS versions. Has anyone else experienced this behavior? If so, are there any known workarounds or fixes? It's crucial for ensuring a consistent user experience across mode transitions.

Thank you for your insights and assistance!

I did test on simulator 16.2 with Xcode 14.2 and did not observe the reset to 0.0.

Do you test on device or simulator ?

Could you detail steps to reproduce, with exact place where you set the alpha value ? And possibly complete code to reproduce ?

I've encountered a challenge in replicating the alpha resetting issue within a basic app setup. The particular screen where this issue arises is intricate, and I'm constrained by time to simplify it to the root cause.

However, a similar problem is evident with NSLayoutConstraint. In my storyboard, there's a button. Initially, it's vertically centered using the constraint buttonCenterConstraintInitiallyInstalledInStoryBoard. Additionally, there's another constraint, topConstraintInitiallyUnistalledInStoryBoard, which positions the button at the top but is uninstalled by default.

On pressing the button, I switch between these constraints, animating the button's movement from the center to the top and vice versa. This works as expected. But, if I activate the initially uninstalled constraint by pressing the button and then either toggle between light and dark modes or rotate the device, the topConstraintInitiallyUnistalledInStoryBoard reverts to its uninstalled state, reactivating the other constraint. Fortunately, this behavior is consistent on both a physical device and an iOS 16.4 simulator. I'm open to sharing the code if there's any interest.

rotate the device, the topConstraintInitiallyUnistalledInStoryBoard reverts to its uninstalled state

That seems logical. As constraints may change on rotation (switch from hRwc to hRwR for instance), constraint manager has to rebuild the constraints. So you should have a code to set constraints, that you call initially and after rotation.

Here's the test project.

The main storyboard:

<?xml version="1.0" encoding="UTF-8"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r"> <device id="retina6_12" orientation="portrait" appearance="light"/> <dependencies> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/> <capability name="Named colors" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <scenes> <!--View Controller--> <scene sceneID="tne-QT-ifu"> <objects> <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="storyboardTest" customModuleProvider="target" sceneMemberID="viewController"> <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> <rect key="frame" x="0.0" y="0.0" width="393" height="852"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7gf-ej-MDN"> <rect key="frame" x="20" y="404" width="353" height="44"/> <color key="backgroundColor" name="MyButtonBackgroundColor"/> <constraints> <constraint firstAttribute="height" constant="44" id="cG3-9X-iJ1"/> </constraints> <fontDescription key="fontDescription" type="system" pointSize="17"/> <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> <state key="normal" title="I'm in the center. Press me!"/> <connections> <action selector="pressMeAction:" destination="BYZ-38-t0r" eventType="primaryActionTriggered" id="njP-Qe-NVb"/> </connections> </button> </subviews> <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/> <constraints> <constraint firstItem="7gf-ej-MDN" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="20" id="B5S-cT-SZB"/> <constraint firstItem="7gf-ej-MDN" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="Uk8-ym-Ly9"/> <constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="7gf-ej-MDN" secondAttribute="trailing" constant="20" id="ui8-Va-MSO"/> <constraint firstItem="7gf-ej-MDN" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="zlv-vV-DH3"/> </constraints> <variation key="default"> <mask key="constraints"> <exclude reference="Uk8-ym-Ly9"/> </mask> </variation> </view> <connections> <outlet property="buttonCenterConstraintInitiallyInstalledInStoryBoard" destination="zlv-vV-DH3" id="a7I-BO-bIy"/> <outlet property="buttonTopConstraintInitiallyUnistalledInStoryBoard" destination="Uk8-ym-Ly9" id="X97-7G-ekb"/> <outlet property="triggerForBugButton" destination="7gf-ej-MDN" id="Qwg-8B-grI"/> </connections> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> </objects> <point key="canvasLocation" x="-244" y="-65"/> </scene> </scenes> <resources> <namedColor name="MyButtonBackgroundColor"> <color red="0.23529411764705882" green="0.23529411764705882" blue="0.23529411764705882" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </namedColor> </resources> </document>

Here's the viewContoller: // // ViewController.swift // storyboardTest // // Created by Salvatore Cataudella on 8/16/23. //

import UIKit

class ViewController: UIViewController {

@IBOutlet var triggerForBugButton: UIButton!

@IBOutlet weak var buttonTopConstraintInitiallyUnistalledInStoryBoard: NSLayoutConstraint!
@IBOutlet weak var buttonCenterConstraintInitiallyInstalledInStoryBoard: NSLayoutConstraint!

var initialText: String? = nil
var toggle: Bool = false

@IBAction func pressMeAction(_ sender: UIButton) {
    toggle = !toggle
    if toggle {
        sender.setTitle("I'm at the top. Press me!", for: .normal)
        buttonCenterConstraintInitiallyInstalledInStoryBoard.isActive = false
        buttonTopConstraintInitiallyUnistalledInStoryBoard.isActive = true
    } else {
        if let title = initialText {
            sender.setTitle(title, for: .normal)
        }
        buttonTopConstraintInitiallyUnistalledInStoryBoard.isActive = false
        buttonCenterConstraintInitiallyInstalledInStoryBoard.isActive = true
    }
    UIView.animate(withDuration: 0.4) {
        self.view.layoutIfNeeded()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    initialText = triggerForBugButton.title(for: .normal)
}

}

Here's the color from the color catalog. Its name is "MyButtonBackgroundColor". It's a single color, but this issue also happens when the UIColor form the catalog has Any, Dark and Light sub colors (file Contents.json): { "colors" : [ { "color" : { "color-space" : "srgb", "components" : { "alpha" : "1.000", "blue" : "60", "green" : "60", "red" : "60" } }, "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } }

Alpha Value of UIButton set in a storyboard is reset on Light/Dark Mode Transition
 
 
Q