All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Replaced the internal
KeyboardObserverwith https://github.com/square/swift-keyboard-observer.
accessibilityCustomContentis an IUO and nils should be handled accordingly.
- Added
BlueprintUIAccessibilityCoremodule with accessibility composition and deferral patterns- Accessibility composition for combining multiple elements into unified experiences
- Accessibility deferral for content inheritance patterns
- Revert applying empty accessibility traits to
AttributedLabelif supplied traits isnil.
- Added a
scrollableAxesSafeAreaEdgesproperty toScrollViewthat is used withContentInsetAdjustmentBehavior.scrollableAxes.
- Applied empty accessibility traits to
AttributedLabelif supplied traits isnil. - Fixed warning for a cross-module conformance
- Fixed warning by updating Package.swift to use the newer
swiftLanguageModes
- Exposed largeContentViewerInteraction on LargeContentViewer backing view via protocol.
- Asserted when large content viewer is not placed inside an interaction container.
- Stopped installing xcodes in GitHub Actions file.
- Added support for accessibility large content viewer.
- Fixed bounding rects for VoiceOver when an attributed label's link spans more than one line.
- Fixed an issue where resizing a
ScrollViewcould result in its scroll position being adjusted incorrectly. - Fixed an issue where a dismissed keyboard could impact a
ScrollView's bottom safe area inset. - Fixed
AccessibilityContainerto better handle the accessibility ordering for aUITableViewor aUICollectionViewinside it (such as aListableinstance).
- Added support for tabbing through links in
AttributedLabel
- Layouts can define custom traits by creating types that conform to
LayoutTraitsKey. - The
SingleTraitLayoutprotocol preserves the existing API for legacy layouts that define a single trait type.
LayoutMode.legacyhas been removed.- The
LegacyLayoutprotocol has been removed, and its methods no longer required to implementLayout. Layouts can remove their implementations of themeasureandlayoutmethods. ConstrainedAspectRatio.ContentMode.fillParenthas been removed.
- With the removal of legacy layout,
Layoutno longer conforms toSingleTraitLayoutby default. Existing layouts that define traits must conform toSingleTraitLayoutexplicitly. LayoutModeconverted from an enum to a struct withLayoutOptionsavailable as a property.
AnimationAttributescan now be initialized with more types of animations, including bezier curves, springs, and dampened springs.
- Removed
AnimationAttributes.curve. - Removed
AnimationAttributes.duration.
- Reverted the change titled "Fixed
AccessibilityContainerto better handle the accessibility ordering for aUICollectionViewinside it (such as aListableinstance.)" pending further investigation.
- Fixed
AccessibilityContainerto better handle the accessibility ordering for aUICollectionViewinside it (such as aListableinstance.)
- Added
UserInterfaceStyleOverridingElementwhich allows child elements to have theiruserInterfaceStyleto be forced to light/dark. Additionally added aoverrideUserInterfaceStyleconvenience toElement.
AccessibilityElement.deprecated_accessibility(…). This was deprecated in September 2021, and renamed from .accessibility(…) to .deprecated_accessibility(…) in Oct 2024.
Accessibility.Traitnow includes.backButtonand.toggleButton
AccessibilityElement.Traitnow a typealias toAccessibility.TraitAccessibilityElement.CustomActionnow a typealias toAccessibility.CustomActionAccessibilityElement.CustomContentnow a typealias toAccessibility.CustomContent
Accessibility.CustomContentnow conforms toEquatable
- Fixed a bug in
AttributedLabelwhich could cause a crash if the attributed string lacked a specifiedNSTextAlignment.
AccessibilityContainernow supports configuration ofUIAccessibilityContainerType,AccessibilityLabelandAccessibilityValue.AccessibilityElementnow supports configuration ofuserInputLabels.
- CocoaPods podspecs removed. Blueprint will only be vended via Swift Package Manager.
AttributedLabel accessibility links are now stateless.
LayoutMode.legacyis deprecated and will be removed in a future release.
- Local development environment switched from CocoaPods to Tuist.
- Added
CacheCleanerwhich exposes a method to force Blueprint's static caches to be cleared.
- The
accessibilityIdentifiercan now be set onAttributedLabel.
- Added release and changelog managements scripts to streamline releases.
Flowchildren now support a layoutpriority, to specify if they should grow to use the extra space in a run.
- Bump CI Xcode version to 15.4.
BlueprintViewhas added preconditions to some methods to ensure they are only invoked on the main queue.
- Renamed deprecated function
accessibility(label:value:traits:hint:identifier:accessibilityFrameSize:)todeprecated_accessibility(label:value:traits:hint:identifier:accessibilityFrameSize:).
BlueprintViewwill now pass through touches to views lower in the view hierarchy ifpassThroughTouchesis true.
- Moved
CornerStyleout of theBoxnamespace, and is now a root type inBlueprintUICommonControls.Box.CornerStyleis still available as a typealias.
- Made public the
UIBezierPathconvenience init that uses aBox.CornerStyle.
LabelandAttributedLabelnow supportaccessibilityValue.
- Fix a bug in which newlines were preserved in accessibility labels.
- Fixed a string range bug when a closed range should be half open.
- Fixed a bug where
AttributedLabel's accessibility utterance was not properly announcing links.
- Fixed a bug where defining a
Boxwith a.roundedCornerStylewithCornersset to anything other than.allwould sometimes still round all of the corners.
AccessibilityElement.CustomContentnow exposes previously internal methods for creatingAXCustomContentobjects.- Introduced a new
Flowlayout type, for creating flow layout based elements.
AttributedLabelnow activates a single contained link when activated by accessible technologies.AccessibilityElement.CustomContent.Importance.Regularrenamed toDefault.
- Fixed a bug where
AccessibilityBlockerwould block accessibility whenisBlockingisfalse.
- Added support for accessibility focus triggers to force VoiceOver to focus on any given element.
- Added
startTimestamptoBlueprintViewRenderMetrics. This represents the mach time in seconds at which the render started, fromCACurrentMediaTime().
- Fixed an issue where
AttributedLabelwould not properly handle tapping on links when a label was stretched.
AccessibilityElementnow supports providing arbitrary strings to assistive devices using theAXCustomContentprotocol.
- The behavior of
nameofElementPreviewhas been change, affecting the SwiftUIpreviewName. Instead of including device or size information (i.e.sizeThatFits - \(name)), it now either defaults to the Xcode default if given an empty string, and shows only thenameifnameis non-empty. - Updated minimum deployment target from iOS 14 to iOS 15.
- Updated CI to use M1 machines, Xcode 15.1, and Ruby 3.2.2.
- Added iOS 17 snapshot images.
- Bump Swift version to 5.9.
- Update Ruby gems.
- Fixed a bug that could cause a crash or incorrect layout when an element with lazily resolved content (such as
GeometryReader) generated a subtree that varied within a layout pass. ([#468])
- Added a
TintAdjustmentModeelement and.tintAdjustmentMode(:)modifier for finer control of tint color during modal presentations.
- Resolved a Swift 5.9 compilation warning: Forming 'UnsafeRawPointer' to a variable of type 'NSObject'; this is likely incorrect because 'NSObject' may contain an object reference.
KeyboardObserverhas been updated to handle iOS 16.1+ changes that use the screen's coordinate space to report keyboard position. This can impact reported values when the app isn't full screen in Split View, Slide Over, and Stage Manager.
- Lifecycle callbacks like
onAppearandonDisappearnow occur outside of the layout pass; allowing, eg,onAppearto safely trigger a re-render.
- Update CI script to reference the
xcodesorg/made/xcodespackage for installing simulator runtimes. - Corrected a typo in
AttributedLabel, which now exits paragraph style enumeration after encountering the first paragraph style. This is an optimization and not a functional change. The method continues to accept only a paragraph style which spans the length of the attributed string.
ConstrainedAspectRatiomeasures correctly infitParentandfillParentmodes when the proposed constraint has the same aspect ratio as the element's constraint.ConstrainedAspectRatioadheres to the Caffeinated Layout contract when unconstrained infitParentorfillParent, by reportinginfinityinstead of falling back to the constrained element's size.
- Caffeinated Layout is enabled by default. You can disable it on a
BlueprintViewwith thelayoutModeproperty, or disable it globally by settingLayoutMode.default.
ConstrainedAspectRatiocontent modefillParentis deprecated, due to having limited utility in Caffeinated Layout.
- Restored documentation generation by executing the generate_docs.sh script with
bundle execto ensure gems are referenced properly.
-
Introduced a new layout engine, Caffeinated Layout. Caffeinated Layout features a new API for custom layouts that is modeled after SwiftUI, and greatly improves performance.
To enable Caffeinated Layout globally, set
LayoutMode.defaultto.caffeinated. To enable it for a single view, set thelayoutModeproperty of aBlueprintView.Caffeinated Layout is not enabled by default yet, but will be in a future release.
-
Added
layoutModetoBlueprintViewRenderMetricsto expose which layout mode was used to render a Blueprint view.
-
The
LayoutandSingleChildLayoutprotocols have new methods to support Caffeinated Layout.To improve performance, Caffeinated Layout requires elements to adhere to a new contract for sizing behavior. Many elements can be easily adapted to the new API, but certain behaviors are no longer possible, particularly with regard to behavior when the size constraint is
unconstrained.For more information about implementing these protocols and the sizing contract, see the
Layoutdocumentation.
- Updated jazzy gem (0.14.3).
- Updated cocoapods (1.12.0).
- Updated Ruby version (2.7).
- The
Environmentwill now automatically inherit forBlueprintViewinstances nested insideUIViewElementduring measurement.
- Introduced
ElementContent.init(byMeasuring:), for use when yourElementcontains a nestedBlueprintView, commonly used to implement stateful elements. This avoids detached measurements and improves performance.
- Renamed
BlueprintViewUpdateMetricstoBlueprintViewRenderMetrics. - Renamed
BlueprintViewRenderMetrics.measureDurationtolayoutDuration. - Renamed
BlueprintViewMetricsDelegate.blueprintView(_:completedUpdateWith:)toblueprintView(_:completedRenderWith:). BlueprintViewRenderMetricsvalues are now calculated usingCACurrentMediaTimeinstead ofDate.
- Added an Internal section to the changelog. This section is intended to capture any notable non-public changes to the project.
ElementContent.init(child:)now utilizes a uniqueContentStoragetype, which improves layout performance.
Image'saspectFillcontentModenow measures the same asaspectFitto avoid aggressively taking up space when not necessary.
- Fixed unexpected measurement results that could occur from
Images using theaspectFitcontentMode.
- Fix Catalyst version specifier in SPM package.
- Added
AccessibilityElement.CustomActionto allow custom actions for use by assistive technologies. - Added
accessibilityCustomActionsproperty toLabelandAttributedLabel. UIViewElementContextnow passes through anenvironmentproperty, enabling environment-dependent measurements and layouts.
- Updated minimum deployment target from iOS 12 to iOS 14.
URLHandlerEnvironmentKey.defaultValueshould now be a no-op in extensions.- Marks pod as
APPLICATION_EXTENSION_API_ONLY
accessibilityHintproperty forLabelandAttributedLabel.
- When stacks lay out with more
fixedmagnitude than is available for layout,flexibleitems will no longer receive a negative width.
StackElementlayouts have been optimized for the case of one fixed and one flexible element to improve performance. This also fixes issues as described in #265 in many cases.
- Improve
AttributedLabelrendering performance.
- Fixed an issue where rounding was handled incorrectly for nested BlueprintViews.
- Added new logging option to expose aggregate measurements.
AccessibilityContainernow omits accessibility elements where.accessibilityElementsHiddenistrue.
Imagenow provides an override to prevent VoiceOver from generating accessibility descriptions.
- Introduced an
Element.modify { ... }conditional, to allow changing properties on an element.
-
Alignedwill now constrain its content to the provided layout frame. If you need content to exceed the layout frame, please useDecoration. -
AccessibilityElementwill now only returnaccessibilityPathfor elements with a non-square corner style. This avoids needlessly changing AccessibilitySnapshot (https://github.com/cashapp/AccessibilitySnapshot) reference images.
- Introduced
accessibilityFrameCornerStyletoAccessibilityElement.
- Added
.growsand.shrinkstoStackLayout.Child.Priority, to allow for extra control over how flexible elements grow and shrink. AccessibilityBlockernow takes in aBoolto control blocking, to avoid changing the element hierarchy to toggle if blocking is occurring.
- Added support for optionals in builders without unwrapping via
if let. - Static constants on
Alignmentare now public (such asAlignment.topTrailing).
AnimationAttributeshas gained a.defaultoption.LayoutTransitionhas default values for itsAnimationAttributesparameters.
- Added support for adjusting text spacing and sizing on
AttributedLabelandLabelwhen text does not fit within the provided layout rect.
MeasurementCachingKeyhas been removed – Blueprint has cached measurements per render pass for many releases, so this actually slowed down layouts due to additional allocations and cache checking. This is about a 5-10% performance improvement depending on the layout.
- Accessibility increment, decrement actions have been moved to associated values on the
AccessibilityElement.Traitenum.
- Shadows on
LabelandAttributedLabel - Accessibility increment, decrement and activate actions now available on
AccessibilityElement Decoratehas a newalignedpositioning, that uses stack-styleAlignmentvalues and alignment guides.- The context vended to custom
Decoratepositions includes the decorated content size.
- The context vended to custom
Decoratepositions was renamed toPositionContext, and thecontentFrameproperty was replaced with acontentSize.
Decoratewill now properly scale its base content to the full size of the rendered element, if the measured and laid out sizes differ.- Fixed an issue where
AttributedLabelcould cause a crash when voice over was enabled.
LayoutWriter.Contextnow exposes the layout phase, to differ any calculations between measurement and layout.
- Fixed an issue where
AttributedLabelandLabelwould not pass touches to super views when expected.
- Fixed an issue where
AttributedLabelmight not detect link taps in multi-line labels. .aligned(vertically:horizontally:)now has the correct default values to match theAlignedinitializer.
- The default line break mode for
Labelis nowbyTruncatingTail, matching the default forUILabel. (It was previouslybyWordWrapping, which does not indicate that truncation occured.) AttributedLabelwill normalize certain line break modes based on the number of lines.
LabelandAttributedLabelnow correctly report theirUIAccessibilityTraits.
- Added the
EditingMenuelement, which allows showing aUIMenuController(aka the system editing menu) on tap, long press, or based on a trigger.
Label.fontnow defaults to using a font of sizeUIFont.labelFontSize(17) instead ofUIFont.systemFontSize.
- Support
CALayerCornerCurveforBoxcorner styles. - Added
AttributedText, which supports applying strongly-typed attributes to strings (much like theAttributedStringtype introduced in iOS 15). - Added support for links to
AttributedLabel:- Links can be added using the
linkattribute of the attributed string. This attribute supportsURLs orStrings. - The label also supports detecting certain types of data and links, much like UITextView. Use the
linkDetectionTypesproperty to specify which types of data to detect. - Links are opened using the
LinkHandlerin the environment, which by default usesUIApplication.shared.open(_:options:completionHandler:). Customize link handling by providing aURLHandlerto the environment at the appropriate scope.AttributedLabelalso has a function for easily handling links with a closure using theonLinkTappedmethod.
- Links can be added using the
- Fixed an issue where
Boxdid not implicitly animate its shadow.
- Reverted scroll view keyboard inset behavior to the behavior in
0.30.0, since the recent changes were causing unexpected issues.
- Fixed an issue where
ScrollViewdid not adjust its content inset correctly when the keyboard height or content insets changed.
- Fixed an issue where
BlueprintViewwould not size correctly when used with Auto Layout.
- Added an
ElementContentvariant whosemeasureFunctiontakes in both aSizeConstraintand anEnvironment.
- Allow measuring within an explicit
SizeConstraintinGeometryProxy. - Add an additional
stackLayoutChild(priority:)method overload, for easier autocomplete when only customizing the layout priority.
- Values returned from
sizeThatFitsandsystemLayoutSizeFittingare now cached.
- Fixed an issue where the keyboard inset adjustment was incorrect in some cases.
- Fixed a retain cycle in
@FocusState. (#285)
- Add support for
for...inloops andavailablechecks to result builder APIs.
intrinsicContentSizeis now cached.
- Improved error messages when using result builders with optional values.
- Added a
Hiddenelement and.hidden()modifier for hiding elements. Overlaynow supports result builders.SegmentedControlnow supports result builders.
- Removed deprecated initializer from
AccessibilityElementwhich was causing ambiguous initializer errors.
UserInteractionEnabledhas been moved fromBlueprintUICommonControlsintoBlueprintUI. It no longer has a backing view, and instead uses layout attributes to apply itself to elements. This change shouldn't affect consumers.
-
The
@FocusStateproperty wrapper can be used to manage focus of text fields. ([#259])After binding a text field to a state, you can programmatically focus the field by setting the state value.
struct LoginForm: ProxyElement { enum Field: Hashable { case username, password } @FocusState private var focusedField: Field? var elementRepresentation: Element { // This text field will be focused when `self.focusedField = .username` TextField(text: "") .focused(when: $focusedField, equals: .username) } }
-
Row, Column, EqualStack, and GridRow can now be initialized declaratively using result builders. ([#220])
- To declare one of these containers, simply include the elements inside the
ElementBuildertrailing closure. - To customize the container, pass values through the containers
initor leave out to use the provided defaults parameters. - To customize one of the child element's container specific properties (key, priority, etc), tack on a corresponding modifier such as
stackLayoutChild()andgridRowChild().
let row = Row(alignment: .fill) { TestElement() TestElement2() .stackLayoutChild(priority: .fixed, alignmentGuide: { _ in 0 }, key: "two") }
- To declare one of these containers, simply include the elements inside the
-
The
accessibilityElement(...)modifier has been added for wrapping anElementin anAccessibilityElement. Note that this will override all accessibility parameters of theElementbeing wrapped, even if values are left unspecified or set tonil. -
An initializer on
AccessibilityElementthat requires alabel,value, andtraits. -
Overlaysupports keys for disambiguation between view updates. ([#264])
-
BlueprintView'sintrinsicContentSizewill now returnUIView.noIntrinsicMetricif there is noelementassociated with it. -
TextField'sbecomeActiveTriggerandresignActiveTriggerproperties have been replaced with afocusBindingfor use with the new@FocusStateproperty wrapper.
- The
accessibility(...)modifier has been deprecated. UseaccessibilityElement(...)instead. - An initializer on
AccessibilityElementthat allowed all parameters to be unspecified. Use the initializer with required parameters instead. Overlay.add(_:)deprecated in favor ofOverlay.add(key:child:).
- View-backed elements may opt-in to a frame rounding behavior that prioritizes preserving the frame size rather than the frame edges. This is primarily meant for text labels, to fix an issue where labels gain or lose a pixel in rounding and become wrapped or truncated incorrectly. ([#257])
- Lifecycle hooks are guaranteed to run after all views are updated. ([#260])
- Fixed an issue where
LabelandAttributedLabelwere not accessibility elements.
-
LabelAttributedLabelandTextFieldelements now support configuration of accessibility traits. -
The
Environmentis now automatically propagated through to nestedBlueprintViewswithin a displayedElementhierarchy. This means that if your view-backedElementsthemselves contain aBlueprintView(eg to manage their own state), that nested view will now automatically receive the correctEnvironmentacrossBlueprintViewboundaries. If you were previously manually propagatingEnvironmentvalues you may remove this code. If you would like to opt-out of this behavior; you can setview.automaticallyInheritsEnvironmentFromContainingBlueprintViews = falseon yourBlueprintView. -
[Lifecycle hooks][#244]. You can hook into lifecycle events when an element's visibility changes.
element .onAppear { // runs when `element` appears } .onDisappear { // runs when `element` disappears }
makeUIView()onUIViewElementis no longer a static function, to allow access to properties during view creation.
- The signature of
Element.backingViewDescription(bounds: CGRect, subtreeExtent: CGRect?) -> ViewDescription?has changed tobackingViewDescription(with context: ViewDescriptionContext) -> ViewDescription?(#231). This is a large breaking change, but is worth it as it allows us to pass additional context tobackingViewDescriptionin the future in a non-breaking way. TheViewDescriptionContextcontains theboundsandsubtreeExtent, as well as theEnvironmentthe element is rendered in.
- Add
ScrollTrigger, which adds the ability set the content offset of aScrollView - Add
UIViewElementContexttoUIViewElement.updateUIView. The context currently has one property,isMeasuring, which tells you if the view being updated is the static measuring instance.
- Expose
contentInsetAdjustmentBehavioronScrollView.
-
Add
Keyedelement, which can be used to help differentiate elements during the diff and update process, eg to assist with proper animation transitions. -
Introduce
GridRow, aRowalternative suited for columnar layout.GridRowsupports the following:- spacing
- vertical alignment
- children with absolutely-sized widths
- children with proportionally-sized widths¹
¹Proportional width in this case always means "a proportion of available layout space after spacing and absolutely-sized children are laid out."
Example:
GridRow { row in row.spacing = 8 row.verticalAlignment = .center row.add(width: .absolute(50), child: authorLabel) row.add(width: .proportional(0.75), child: bodyLabel) row.add(width: .proportional(0.25), child: dateLabel) }
-
Added support to
LayoutWriterto allow specifying keys for childElements. -
Blueprint can now emit signpost logs during its render pass, which you can use for performance tuning. ([#209])
Signpost logs are disabled by default. To enable them, set
BlueprintLogging.enabled = true.
- The layout system now uses a caching system to improve performance by eliminating redundant measurements. ([#209])
- Introduce
UserInteractionEnabled, an element which conditionally enables user interaction of wrapped elements.
searchField
.userInteractionEnabled(canBeginSearch)-
Change
ProxyElementto directly return the content of a child. This significantly speeds up deeper element hierarchies that are made up of proxy elements, by reducing the duplicate calculation work that needs to be done to layout an element tree. -
Change backing view of
TransitionContainerto not directly receive touches while still allowing subviews to do so.
- Introduce
Decorateto allow placing a decoration element in front or behind of anElement, without affecting its layout. This is useful for rendering tap or selection states which should overflow the natural bounds of theElement, similar to a shadow, or useful for adding a badge to anElement.
- Introduce conditionals on
Elementto allow you to perform inline checks likeif,if let, andmapwhen buildingElementtrees.
- Introduce additional APIs on
Overlayto ease conditional construction theOverlayelements.
- Add
Transformedelement to apply aCATransform3Dto a wrapped element.
- Remove compile time validation from
Elements, since it caused compile-time errors in certain cases when extensions andwhereclauses were used.
Ensure thatElements are a value type. This is generally assumed by Blueprint, but was previously not validated. This is only validated inDEBUGbuilds, to avoid otherwise affecting performance.
- Add
LayoutWriter, which makes creating custom / arbitrary layouts much simpler. You no longer need to define a customLayouttype; instead, you can just utilizeLayoutWriterand configure and place your children within its builder initializer.
- Add
AccessibilityContainer.identifier([#180])
-
Fixed an issue where view descriptions were applied with unintentional animations while creating backing views. This could happen if an element was added during a transition. ([#175])
-
Fixed pull-to-refresh inset for iOS 13+. ([#176])
-
Add alignment guides to stacks. ([#153])
Alignment guides let you fine-tune the cross axis alignment. You can specifying a guide value for any child in that element's coordinate space. Children are aligned relatively to each other so that the guide values line up, and then the content as a whole is aligned to the stack's bounds.
In this example, the center of one element is aligned 10 points from the bottom of another element, and the contents are collectively aligned to the bottom of the row:
Row { row in row.verticalAlignment = .bottom row.add( alignmentGuide: { d in d[VerticalAlignment.center] }, child: element1 ) row.add( alignmentGuide: { d in d.height - 10 }, child: element2 ) }
- Removed support for iOS 10. Future releases will only support iOS 11 and later.
Rowalignmentsleadingandtrailingare deprecated. Usetopandbottominstead. ([#153])
- Fixed
EqualStackto properly constrain children when measuring. (#157)
-
Add a new
TransitionContainer.initthat supports further customization during initialization and has the same defaults asViewDescription. ([#155], [#158]) -
Add
transition(onAppear:onDisappear:onLayout)andtransition(_:)methods toElementto describe transition animations. ([#155], [#158])
- Remove
GridLayout; it's incomplete and was never really intended to be consumed widely. The intended replacement is puttingEqualStacksinside of aColumn, orRowsinside aColumn.
TransitionContainer(wrapping:)is deprecated. Use the newTransitionContainer(transitioning:)instead. ([#158])
- Removed some redundant work being done during rendering. ([#154])
- Fixes a crash that can occur in
Boxwhen specifying a corner radius and shadow. ([#149])
-
Add
addFixed(child:)andaddFlexible(child:)methods toStackElementfor adding children with a grow & shrink priority of 0.0 and 1.0 respectively. ([#143]) -
Add
capsulecase toBox.CornerStyle([#145]). This addition sugars the following pattern:GeometryReader { geometry in Box(cornerStyle: .rounded(geometry.constraint.height.maximum / 2.0)) }
into
Box(cornerStyle: .capsule)
-
Add
accessibilityFrameSizetoAccessibilityElementfor manually specifying a size for the frame rendered by Voice Over. ([#144]) -
Add
Opacityelement for modifying the opacity of a wrapped element. ([#147])
BlueprintViewwill calllayoutIfNeededon backing views during its layout pass. This allows backing views' subviews that are laid out duringlayoutSubviewsto participate in animations. ([#139])
-
Add
textColorproperty on TextField (#133). -
Add the
windowSizeenvironment key. ([#134]) -
Add
GeometryReader. ([#135])This element allow you to compose elements whose contents depend on the amount of space available.
Here is an example that dynamically chooses an image based on the width available:
GeometryReader { (geometry) -> Element in let image: UIImage switch geometry.constraint.width.maximum { case ..<100: image = UIImage(named: "small")! case 100..<500: image = UIImage(named: "medium")! default: image = UIImage(named: "large")! } return Image(image: image) }
- Default
ScrollView.delaysContentTouchestotrue(#132)
- Set an explicit shadow path on
Box(#137)
-
Introduce
AccessibilityContainerelement for wrapping an element with multiple sub-elements that should be in a voice over container. -
Add
fontproperty on TextField (#127).
-
Update the scroll indicator inset when adjusting the content inset.
-
Label&AttributedLabeluse an internalUILabelfor measurement. This fixes measurement when there is a line limit set. However, it also means that the screen scale cannot be specified and is always assumed to beUIScreen.main.scale. These elements may not be measured correctly if they are placed on a screen other thanUIScreen.main. ([#120])
-
Introduce MeasurementCachingKey, to allow for elements to provide a way to cache their measurement during layout passes. This provides performance optimizations for elements whose layout and measurement is expensive to calculate.
-
Introduce
UIViewElementto make wrapping self-sizing UIViews easier.You can now write a
UIViewElementlike this:struct Switch : UIViewElement { var isOn : Bool typealias UIViewType = UISwitch static func makeUIView() -> UISwitch { UISwitch() } func updateUIView(_ view: UISwitch) { view.isOn = self.isOn } }And the elements will be sized and presented correctly based on the view's
sizeThatFits. -
Add
isAccessibilityElementtoLabelandAttributedLabel. ([#120]) -
Add
lineHeighttoLabelfor specifying custom line heights.AttributedLabelhas atextRectOffsetproperty to support this. ([#120])
- Update Demo app to support more demo screen types.
- Fix erroneous use of
frameinstead ofboundswhen laying outBlueprintView.
- Add delaysContentTouches to the
ScrollViewelement.
- Use default environment when measuring
BlueprintView.
- Removed layout rounding no longer needed since [#64] ([#95]).
-
Add support for the iPhone SE 2 in
ElementPreview. -
Added
tintColorandcontentModeinto the initializer forImage. ([#100]) -
Added an Empty element, to mirror
EmptyViewin SwiftUI. It is an element with no size and draws no content. -
Environment ([#101]).
You can now read and write values from an
Environmentthat is automatically propagated down the element tree. You can use these values to dynamically build the contents of an element, without having to explicitly pass every value through the tree yourself.You can read these values with
EnvironmentReader:struct Foo: ProxyElement { var elementRepresentation: Element { EnvironmentReader { environment -> Element in Label(text: "value from environment: \(environment.foo)") } } }
And set them with
AdaptedEnvironment:struct Bar: ProxyElement { var elementRepresentation: Element { ComplicatedElement() .adaptedEnvironment { environment in environment.foo = "bar" } } }
Several enviroment keys are available by default ([#102]):
- calendar
- display scale
- layout direction
- locale
- safe area insets
- time zone
You can create your own by making a type that conforms to
EnvironmentKey, and extendingEnvironmentwith a new property:extension Environment { private enum FooKey: EnvironmentKey { static let defaultValue: String = "default value" } /// The current Foo that elements should use. public var foo: String { get { self[FooKey.self] } set { self[FooKey.self] = newValue } } }
-
Fixed
ConstrainedSizeto ensure that the measurement of its inner element respects theConstrainedSize's maximum size, which matters for measuring elements which re-flow based on width, such as elements containing text. -
Changed BlueprintView.sizeThatFits(:) to treat width and height separately when determining if measurement should be unconstrained in a given axis.
-
Added support for
SwiftUI-style element building withinBlueprintUIandBlueprintUICommonControls.This allows you to replace this code:
ScrollView(.fittingHeight) ( wrapping: Box( backgroundColor .lightGrey, wrapping: Inset( uniformInset: 10.0, wrapping: ConstrainedSize( height: .atLeast(20.0), wrapping: Label( text: "Hello, world!" ) ) ) ) )
With this code:
Label(text: "Hello, World!") .constrainedTo(height: .atLeast(20.0)) .inset(by: 20.0) .box(background: .lightGrey) .scrollable(.fittingHeight)
Improving readability and conciseness of your elements.
- BlueprintView will align view frames to pixel boundaries after layout ([#64]).
- Don't try to build
SwiftUIpreviews on 32 bit ARM devices –SwiftUIdoes not exist on these devices.
- Weak link
SwiftUIso if an app is not already linkingSwiftUI, it will build correctly.
- Add support for previewing Blueprint elements in Xcode / SwiftUI previews.
- Add accessibilityIdentifier support to
AccessibilityElement.
- Properly measure the child of
ScrollViewto allow for unconstrained measurement. - Fix stack layout during underflow ([#72])
- ScrollView can automatically adjust its content inset for the keyboard ([#55])
- Improved element diffing ([#56])
- Xcode 11 and Swift 5.1 support ([#67]).
- Change how stack [layouts are measured][#68] to resolve an issue where text would be truncated.
- Raise minimum deployment target from iOS 9.3 to iOS 10 ([#66]).
- Add
keyboardDismissModeproperty on ScrollView ([#57]). - Add
textAlignmentproperty on TextField ([#53]).
- Prevent divide-by-zero when a stack contains zero-size elements ([#52]).
- Add
fillalignment to Aligned ([#42]).
- Fix typos in the tutorial ([#46]).
- Add docs to Overlay ([#45]).
- Public init on Aligned ([#41]).
- Guarantee that subviews are ordered the same as their associated elements ([#32]).
- Do not run snapshot tests from CocoaPods ([#40]).
- Make tests with float comparisons more lenient for 32-bit ([#35]).
- Add Swift Package Manager support ([#37]).
- Add Getting Started section to README ([#38]).
- Add Stack alignment options
justifyToStart,justifyToCenter, andjustifyToEnd([#24]). - Add ConstrainedAspectRatio element ([#23]).
- Add EqualStack element ([#26]).
- Add Rule element ([#22]).
- Add Aligned element ([#21]).
- Add a screen scale property to some elements ([#18]).
- Swift 5 support ([#15]).
- Reorder the parameters of ConstrainedSize, Inset, Button, and Tappapble, so that the wrapped element is the last parameter ([#19]).
- First stable release. [main]: https://github.com/square/Blueprint/compare/6.6.0...HEAD [6.6.0]: https://github.com/square/Blueprint/compare/6.5.1...HEAD [6.5.1]: https://github.com/square/Blueprint/compare/6.5.0...6.5.1 [6.5.0]: https://github.com/square/Blueprint/compare/6.4.0...6.5.0 [6.4.0]: https://github.com/square/Blueprint/compare/6.3.1...6.4.0 [6.3.1]: https://github.com/square/Blueprint/compare/6.3.0...6.3.1 [6.3.0]: https://github.com/square/Blueprint/compare/6.2.0...6.3.0 [6.2.0]: https://github.com/square/Blueprint/compare/6.1.0...6.2.0 [6.1.0]: https://github.com/square/Blueprint/compare/6.0.0...6.1.0 [6.0.0]: https://github.com/square/Blueprint/compare/5.7.0...6.0.0 [5.7.0]: https://github.com/square/Blueprint/compare/5.6.0...5.7.0 [5.6.0]: https://github.com/square/Blueprint/compare/5.5.0...5.6.0 [5.5.0]: https://github.com/square/Blueprint/compare/5.4.0...5.5.0 [5.4.0]: https://github.com/square/Blueprint/compare/5.3.0...5.4.0 [5.3.0]: https://github.com/square/Blueprint/compare/5.2.0...5.3.0 [5.2.0]: https://github.com/square/Blueprint/compare/5.1.0...5.2.0 [5.1.0]: https://github.com/square/Blueprint/compare/5.0.1...5.1.0 [5.0.1]: https://github.com/square/Blueprint/compare/5.0.0...5.0.1 [5.0.0]: https://github.com/square/Blueprint/compare/4.3.0...5.0.0 [4.3.0]: https://github.com/square/Blueprint/compare/4.2.1...4.3.0 [4.2.1]: https://github.com/square/Blueprint/compare/4.2.0...4.2.1 [4.2.0]: https://github.com/square/Blueprint/compare/4.1.2...4.2.0 [4.1.2]: https://github.com/square/Blueprint/compare/4.1.1...4.1.2 [4.1.1]: https://github.com/square/Blueprint/compare/4.1.0...4.1.1 [4.1.0]: https://github.com/square/Blueprint/compare/4.0.1...4.1.0 [4.0.1]: https://github.com/square/Blueprint/compare/4.0.0...4.0.1 [4.0.0]: https://github.com/square/Blueprint/compare/3.1.0...4.0.0 [3.1.0]: https://github.com/square/Blueprint/compare/3.0.0...3.1.0 [3.0.0]: https://github.com/square/Blueprint/compare/2.2.0...3.0.0 [2.2.0]: https://github.com/square/Blueprint/compare/2.1.0...2.2.0 [2.1.0]: https://github.com/square/Blueprint/compare/2.0.0...2.1.0 [2.0.0]: https://github.com/square/Blueprint/compare/1.0.0...2.0.0 [1.0.0]: https://github.com/square/Blueprint/compare/0.50.0...1.0.0 [0.50.0]: https://github.com/square/Blueprint/compare/0.49.1...0.50.0 [0.49.1]: https://github.com/square/Blueprint/compare/0.49.0...0.49.1 [0.49.0]: https://github.com/square/Blueprint/compare/0.48.1...0.49.0 [0.48.1]: https://github.com/square/Blueprint/compare/0.48.0...0.48.1 [0.48.0]: https://github.com/square/Blueprint/compare/0.47.0...0.48.0 [0.47.0]: https://github.com/square/Blueprint/compare/0.46.0...0.47.0 [0.46.0]: https://github.com/square/Blueprint/compare/0.45.1...0.46.0 [0.45.1]: https://github.com/square/Blueprint/compare/0.45.0...0.45.1 [0.45.0]: https://github.com/square/Blueprint/compare/0.44.1...0.45.0 [0.44.1]: https://github.com/square/Blueprint/compare/0.44.0...0.44.1 [0.44.0]: https://github.com/square/Blueprint/compare/0.43.0...0.44.0 [0.43.0]: https://github.com/square/Blueprint/compare/0.42.0...0.43.0 [0.42.0]: https://github.com/square/Blueprint/compare/0.41.0...0.42.0 [0.41.0]: https://github.com/square/Blueprint/compare/0.40.0...0.41.0 [0.40.0]: https://github.com/square/Blueprint/compare/0.39.0...0.40.0 [0.39.0]: https://github.com/square/Blueprint/compare/0.38.0...0.39.0 [0.38.0]: https://github.com/square/Blueprint/compare/0.37.0...0.38.0 [0.37.0]: https://github.com/square/Blueprint/compare/0.36.1...0.37.0 [0.36.1]: https://github.com/square/Blueprint/compare/0.36.0...0.36.1 [0.36.0]: https://github.com/square/Blueprint/compare/0.35.1...0.36.0 [0.35.1]: https://github.com/square/Blueprint/compare/0.35.0...0.35.1 [0.35.0]: https://github.com/square/Blueprint/compare/0.34.0...0.35.0 [0.34.0]: https://github.com/square/Blueprint/compare/0.33.3...0.34.0 [0.33.3]: https://github.com/square/Blueprint/compare/0.33.2...0.33.3 [0.33.2]: https://github.com/square/Blueprint/compare/0.33.1...0.33.2 [0.33.1]: https://github.com/square/Blueprint/compare/0.33.0...0.33.1 [0.33.0]: https://github.com/square/Blueprint/compare/0.32.0...0.33.0 [0.32.0]: https://github.com/square/Blueprint/compare/0.31.0...0.32.0 [0.31.0]: https://github.com/square/Blueprint/compare/0.30.0...0.31.0 [0.30.0]: https://github.com/square/Blueprint/compare/0.29.0...0.30.0 [0.29.0]: https://github.com/square/Blueprint/compare/0.28.1...0.29.0 [0.28.1]: https://github.com/square/Blueprint/compare/0.28.0...0.28.1 [0.28.0]: https://github.com/square/Blueprint/compare/0.27.0...0.28.0 [0.27.0]: https://github.com/square/Blueprint/compare/0.26.0...0.27.0 [0.26.0]: https://github.com/square/Blueprint/compare/0.25.0...0.26.0 [0.25.0]: https://github.com/square/Blueprint/compare/0.24.0...0.25.0 [0.24.0]: https://github.com/square/Blueprint/compare/0.23.0...0.24.0 [0.23.0]: https://github.com/square/Blueprint/compare/0.22.0...0.23.0 [0.22.0]: https://github.com/square/Blueprint/compare/0.21.0...0.22.0 [0.21.0]: https://github.com/square/Blueprint/compare/0.20.0...0.21.0 [0.20.0]: https://github.com/square/Blueprint/compare/0.19.1...0.20.0 [0.19.1]: https://github.com/square/Blueprint/compare/0.19.0...0.19.1 [0.19.0]: https://github.com/square/Blueprint/compare/0.18.0...0.19.0 [0.18.0]: https://github.com/square/Blueprint/compare/0.17.1...0.18.0 [0.17.1]: https://github.com/square/Blueprint/compare/0.17.0...0.17.1 [0.17.0]: https://github.com/square/Blueprint/compare/0.16.0...0.17.0 [0.16.0]: https://github.com/square/Blueprint/compare/0.15.1...0.16.0 [0.15.1]: https://github.com/square/Blueprint/compare/0.15.0...0.15.1 [0.15.0]: https://github.com/square/Blueprint/compare/0.14.0...0.15.0 [0.14.0]: https://github.com/square/Blueprint/compare/0.13.1...0.14.0 [0.13.1]: https://github.com/square/Blueprint/compare/0.13.0...0.13.1 [0.13.0]: https://github.com/square/Blueprint/compare/0.12.2...0.13.0 [0.12.2]: https://github.com/square/Blueprint/compare/0.12.1...0.12.2 [0.12.1]: https://github.com/square/Blueprint/compare/0.12.0...0.12.1 [0.12.0]: https://github.com/square/Blueprint/compare/0.11.0...0.12.0 [0.11.0]: https://github.com/square/Blueprint/compare/0.10.0...0.11.0 [0.10.0]: https://github.com/square/Blueprint/compare/0.9.2...0.10.0 [0.9.2]: https://github.com/square/Blueprint/compare/0.9.1...0.9.2 [0.9.1]: https://github.com/square/Blueprint/compare/0.9.0...0.9.1 [0.9.0]: https://github.com/square/Blueprint/compare/0.8.0...0.9.0 [0.8.0]: https://github.com/square/Blueprint/compare/0.7.0...0.8.0 [0.7.0]: https://github.com/square/Blueprint/compare/0.6.0...0.7.0 [0.6.0]: https://github.com/square/Blueprint/compare/0.5.0...0.6.0 [0.5.0]: https://github.com/square/Blueprint/compare/0.4.0...0.5.0 [0.4.0]: https://github.com/square/Blueprint/compare/0.3.1...0.4.0 [0.3.1]: https://github.com/square/Blueprint/compare/0.3.0...0.3.1 [0.3.0]: https://github.com/square/Blueprint/compare/0.2.2...0.3.0 [0.2.2]: https://github.com/square/Blueprint/releases/tag/0.2.2 [#468]: #468 [#264]: #264 [#260]: #260 [#259]: #259 [#257]: #257 [#244]: #244 [#209]: #209 [#176]: #176 [#175]: #175 [#158]: #158 [#155]: #155 [#154]: #154 [#153]: #153 [#149]: #149 [#147]: #147 [#145]: #145 [#144]: #144 [#143]: #143 [#139]: #139 [#135]: #135 [#134]: #134 [#120]: #120 [#102]: #102 [#101]: #101 [#100]: #100 [#95]: #95 [#72]: #72 [#68]: #68 [#67]: #67 [#66]: #66 [#64]: #64 [#57]: #57 [#56]: #56 [#55]: #55 [#53]: #53 [#52]: #52 [#46]: #46 [#45]: #45 [#42]: #42 [#41]: #41 [#40]: #40 [#38]: #38 [#37]: #37 [#35]: #35 [#32]: #32 [#26]: #26 [#24]: #24 [#23]: #23 [#22]: #22 [#21]: #21 [#19]: #19 [#18]: #18 [#15]: #15