From 20ddffe27b8080edde66806ae8eb6f127b3ade48 Mon Sep 17 00:00:00 2001 From: Jon Petersson Date: Tue, 7 Jan 2025 09:48:29 +0100 Subject: [PATCH] Fix multiple small issues in ConnectionView --- ios/MullvadVPN.xcodeproj/project.pbxproj | 4 ++ .../Extensions/View+Conditionals.swift | 41 +++++++++++++++++++ .../Extensions/View+TapAreaSize.swift | 1 - .../ChipView/ChipContainerView.swift | 17 ++++---- .../ChipView/ChipFeature.swift | 4 ++ .../FeatureIndicators/ChipView/ChipView.swift | 2 +- .../ChipView/ChipViewModelProtocol.swift | 30 +++++++------- .../ConnectionView/ButtonPanel.swift | 2 +- .../ConnectionView/ConnectionView.swift | 33 +++++++-------- .../ConnectionViewViewModel.swift | 9 ++-- .../ConnectionView/DetailsContainer.swift | 37 ++++++----------- .../ConnectionView/HeaderView.swift | 10 ++--- .../FeatureIndicatorsView.swift | 11 +++-- ios/MullvadVPN/Views/MainButtonStyle.swift | 4 +- ios/MullvadVPN/Views/SplitMainButton.swift | 5 +++ 15 files changed, 123 insertions(+), 87 deletions(-) create mode 100644 ios/MullvadVPN/Extensions/View+Conditionals.swift diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index dc823844fdeb..080cb5cbd400 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -619,6 +619,7 @@ 7AA1309F2D007B2500640DF9 /* VisualEffectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA1309E2D007B2500640DF9 /* VisualEffectView.swift */; }; 7AA130A12D01B1E200640DF9 /* SplitMainButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA130A02D01B1E200640DF9 /* SplitMainButton.swift */; }; 7AA513862BC91C6B00D081A4 /* LogRotationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */; }; + 7AA636382D2D3BB0009B2C89 /* View+Conditionals.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA636372D2D3BAC009B2C89 /* View+Conditionals.swift */; }; 7AA7046A2C8EFE2B0045699D /* StoredRelays.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA704682C8EFE050045699D /* StoredRelays.swift */; }; 7AB2B6702BA1EB8C00B03E3B /* ListCustomListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB2B66E2BA1EB8C00B03E3B /* ListCustomListViewController.swift */; }; 7AB2B6712BA1EB8C00B03E3B /* ListCustomListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB2B66F2BA1EB8C00B03E3B /* ListCustomListCoordinator.swift */; }; @@ -2002,6 +2003,7 @@ 7AA1309E2D007B2500640DF9 /* VisualEffectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffectView.swift; sourceTree = ""; }; 7AA130A02D01B1E200640DF9 /* SplitMainButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitMainButton.swift; sourceTree = ""; }; 7AA513852BC91C6B00D081A4 /* LogRotationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogRotationTests.swift; sourceTree = ""; }; + 7AA636372D2D3BAC009B2C89 /* View+Conditionals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Conditionals.swift"; sourceTree = ""; }; 7AA704682C8EFE050045699D /* StoredRelays.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoredRelays.swift; sourceTree = ""; }; 7AB2B66E2BA1EB8C00B03E3B /* ListCustomListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListCustomListViewController.swift; sourceTree = ""; }; 7AB2B66F2BA1EB8C00B03E3B /* ListCustomListCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListCustomListCoordinator.swift; sourceTree = ""; }; @@ -3173,6 +3175,7 @@ 7A21DACE2A30AA3700A787A9 /* UITextField+Appearance.swift */, 5878F4FF29CDA742003D4BE2 /* UIView+AutoLayoutBuilder.swift */, 7A516C2D2B6D357500BBD33D /* URL+Scoping.swift */, + 7AA636372D2D3BAC009B2C89 /* View+Conditionals.swift */, 7A0EAE9D2D01BCBF00D3EB8B /* View+Size.swift */, 7A8A18FA2CE4B66C000BCB5B /* View+TapAreaSize.swift */, ); @@ -5946,6 +5949,7 @@ F01DAE332C2B032A00521E46 /* RelaySelection.swift in Sources */, 58B993B12608A34500BA7811 /* LoginContentView.swift in Sources */, 7A516C2E2B6D357500BBD33D /* URL+Scoping.swift in Sources */, + 7AA636382D2D3BB0009B2C89 /* View+Conditionals.swift in Sources */, 5878A27529093A310096FC88 /* StorePaymentEvent.swift in Sources */, 7A7AD28D29DC677800480EF1 /* FirstTimeLaunch.swift in Sources */, F062000C2CB7EB5D002E6DB9 /* UIImage+Helpers.swift in Sources */, diff --git a/ios/MullvadVPN/Extensions/View+Conditionals.swift b/ios/MullvadVPN/Extensions/View+Conditionals.swift new file mode 100644 index 000000000000..39e4405da8e6 --- /dev/null +++ b/ios/MullvadVPN/Extensions/View+Conditionals.swift @@ -0,0 +1,41 @@ +// +// View+Conditionals.swift +// MullvadVPN +// +// Created by Jon Petersson on 2025-01-07. +// Copyright © 2025 Mullvad VPN AB. All rights reserved. +// + +import SwiftUI + +extension View { + @ViewBuilder func `if`( + _ conditional: Bool, + @ViewBuilder _ content: (Self) -> Content + ) -> some View { + if conditional { + content(self) + } else { + self + } + } + + @ViewBuilder func ifLet( + _ conditional: T?, + @ViewBuilder _ content: (Self, _ value: T) -> Content + ) -> some View { + if let value = conditional { + content(self, value) + } else { + self + } + } + + @ViewBuilder func showIf(_ conditional: Bool) -> some View { + if conditional { + self + } else { + EmptyView() + } + } +} diff --git a/ios/MullvadVPN/Extensions/View+TapAreaSize.swift b/ios/MullvadVPN/Extensions/View+TapAreaSize.swift index bf81cd57be35..c9ab7ee14422 100644 --- a/ios/MullvadVPN/Extensions/View+TapAreaSize.swift +++ b/ios/MullvadVPN/Extensions/View+TapAreaSize.swift @@ -28,6 +28,5 @@ private struct TappablePadding: ViewModifier { height: max(actualViewSize.height, tappableViewSize.height) ) .contentShape(Rectangle()) - .frame(width: actualViewSize.width, height: actualViewSize.height) } } diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipContainerView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipContainerView.swift index 333761fa30ab..cff5cc4c61b6 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipContainerView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipContainerView.swift @@ -30,15 +30,14 @@ struct ChipContainerView: View where ViewModel: ChipViewModelProtocol createChipViews(chips: chipsToAdd, containerWidth: containerWidth) } - if showMoreButton { - Text(LocalizedStringKey("\(viewModel.chips.count - chipsToAdd.count) more...")) - .font(.subheadline) - .lineLimit(1) - .foregroundStyle(UIColor.primaryTextColor.color) - .onTapGesture { - isExpanded.toggle() - } - } + Text(LocalizedStringKey("\(viewModel.chips.count - chipsToAdd.count) more...")) + .font(.subheadline) + .lineLimit(1) + .foregroundStyle(UIColor.primaryTextColor.color) + .onTapGesture { + isExpanded.toggle() + } + .showIf(showMoreButton) Spacer() } diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipFeature.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipFeature.swift index ea1d130dbede..a661a2cefc23 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipFeature.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipFeature.swift @@ -8,6 +8,10 @@ import MullvadSettings import SwiftUI +// Opting to use NSLocalizedString instead of LocalizedStringKey here in order +// to be able to fetch the string value at a later point (eg. in ChipViewModelProtocol, +// when calculating the text widths of the chips). + protocol ChipFeature { var isEnabled: Bool { get } var name: String { get } diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipView.swift index 1c6a5eb52267..57fc7cb042d3 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipView.swift @@ -11,7 +11,7 @@ import SwiftUI struct ChipView: View { let item: ChipModel var body: some View { - Text(LocalizedStringKey(item.name)) + Text(item.name) .font(.subheadline) .lineLimit(1) .foregroundStyle(UIColor.primaryTextColor.color) diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipViewModelProtocol.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipViewModelProtocol.swift index fabec7f8c9e9..d8738c69d3ce 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipViewModelProtocol.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ChipView/ChipViewModelProtocol.swift @@ -13,12 +13,19 @@ protocol ChipViewModelProtocol: ObservableObject { } extension ChipViewModelProtocol { - func chipsToAdd(forContainerWidth containerWidth: CGFloat) -> (chips: [ChipModel], chipsWillOverflow: Bool) { + func chipsToAdd(forContainerWidth containerWidth: CGFloat) -> (chips: [ChipModel], isOverflowing: Bool) { var chipsToAdd = [ChipModel]() - var chipsWillOverflow = false - - let moreTextWidth = "\(chips.count) more..." - .width(using: .preferredFont(forTextStyle: .subheadline)) + 4 // Some extra to be safe. + var isOverflowing = false + + let moreTextWidth = String( + format: NSLocalizedString( + "CONNECTION_VIEW_CHIPS_MORE", + tableName: "ConnectionView", + value: "@d more...", + comment: "" + ), arguments: [chips.count] + ) + .width(using: .preferredFont(forTextStyle: .subheadline)) + 4 // Some extra to be safe. var totalChipsWidth: CGFloat = 0 for (index, chip) in chips.enumerated() { @@ -33,20 +40,13 @@ extension ChipViewModelProtocol { let chipWillFitWithMoreText = (totalChipsWidth + moreTextWidth) <= containerWidth let chipWillFit = totalChipsWidth <= containerWidth - if chipWillFitWithMoreText { - // If a chip can fit together with the "more" text, add it. - chipsToAdd.append(chip) - chipsWillOverflow = !isLastChip - } else if chipWillFit && isLastChip { - // If a chip can fit and it's the last one, add it. - chipsToAdd.append(chip) - chipsWillOverflow = false - } else { + guard (chipWillFit && isLastChip) || chipWillFitWithMoreText else { + isOverflowing = true break } } - return (chipsToAdd, chipsWillOverflow) + return (chipsToAdd, isOverflowing) } } diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ButtonPanel.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ButtonPanel.swift index bfbe4370418d..d1b4590ae23c 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ButtonPanel.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ButtonPanel.swift @@ -32,10 +32,10 @@ extension ConnectionView { text: viewModel.localizedTitleForSelectLocationButton, image: .iconReload, style: .default, + accessibilityId: .selectLocationButton, primaryAction: { action?(.selectLocation) }, secondaryAction: { action?(.reconnect) } ) - .accessibilityIdentifier(AccessibilityIdentifier.selectLocationButton.asString) case .disconnecting, .pendingReconnect, .disconnected: MainButton( text: viewModel.localizedTitleForSelectLocationButton, diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionView.swift index 929e0b17a3f2..2409dc2cf26c 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionView.swift @@ -19,10 +19,11 @@ struct ConnectionView: View { var body: some View { Spacer() + .accessibilityIdentifier(AccessibilityIdentifier.connectionView.asString) + VStack(spacing: 22) { - if connectionViewModel.showsActivityIndicator { - CustomProgressView(style: .large) - } + CustomProgressView(style: .large) + .showIf(connectionViewModel.showsActivityIndicator) ZStack { BlurView(style: .dark) @@ -30,13 +31,12 @@ struct ConnectionView: View { VStack(alignment: .leading, spacing: 16) { HeaderView(viewModel: connectionViewModel, isExpanded: $isExpanded) - if connectionViewModel.showConnectionDetails { - DetailsContainer( - viewModel: connectionViewModel, - indicatorsViewModel: indicatorsViewModel, - isExpanded: $isExpanded - ) - } + DetailsContainer( + connectionViewModel: connectionViewModel, + indicatorsViewModel: indicatorsViewModel, + isExpanded: $isExpanded + ) + .showIf(connectionViewModel.showConnectionDetails) ButtonPanel(viewModel: connectionViewModel, action: action) } @@ -45,19 +45,20 @@ struct ConnectionView: View { .cornerRadius(12) .padding(16) } - .padding(.bottom, 8) // Adding some spacing so as not to overlap with the map legal link. - .accessibilityIdentifier(AccessibilityIdentifier.connectionView.asString) + .padding(.bottom, 8) // Some spacing to avoid overlap with the map legal link. .onChange(of: isExpanded) { _ in onContentUpdate?() } .onReceive(connectionViewModel.combinedState) { _, _ in - onContentUpdate?() - + // Only update expanded state when connections details should be hidden. + // This will contract the view on eg. disconnect, but leave it as-is on + // eg. connect. if !connectionViewModel.showConnectionDetails { -// withAnimation { isExpanded = false -// } + return } + + onContentUpdate?() } } } diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewViewModel.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewViewModel.swift index 2f7af8a3b572..962eaa0d63fd 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewViewModel.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/ConnectionViewViewModel.swift @@ -52,9 +52,9 @@ extension ConnectionViewViewModel { var showConnectionDetails: Bool { switch tunnelStatus.state { case .connecting, .reconnecting, .waitingForConnectivity(.noConnection), .negotiatingEphemeralPeer, - .connected, .pendingReconnect, .waitingForConnectivity(.noNetwork): + .connected, .pendingReconnect: true - case .disconnecting, .disconnected, .error: + case .disconnecting, .disconnected, .waitingForConnectivity(.noNetwork), .error: false } } @@ -130,9 +130,10 @@ extension ConnectionViewViewModel { var localizedTitleForSelectLocationButton: LocalizedStringKey { switch tunnelStatus.state { - case .disconnecting, .pendingReconnect, .disconnected: + case .disconnecting, .pendingReconnect, .disconnected, .waitingForConnectivity(.noNetwork): LocalizedStringKey("Select location") - case .connecting, .connected, .reconnecting, .waitingForConnectivity, .negotiatingEphemeralPeer, .error: + case .connecting, .connected, .reconnecting, .waitingForConnectivity(.noConnection), + .negotiatingEphemeralPeer, .error: LocalizedStringKey("Switch location") } } diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsContainer.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsContainer.swift index 7da9aef5b8f6..2597096c33a9 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsContainer.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/DetailsContainer.swift @@ -10,40 +10,29 @@ import SwiftUI extension ConnectionView { internal struct DetailsContainer: View { - @StateObject var viewModel: ConnectionViewViewModel + @StateObject var connectionViewModel: ConnectionViewViewModel @StateObject var indicatorsViewModel: FeatureIndicatorsViewModel @Binding var isExpanded: Bool @State private var scrollViewHeight: CGFloat = 0 var body: some View { -// if isExpanded { Divider() .background(UIColor.secondaryTextColor.color) - .opacity(isExpanded ? 1.0 : 0.0) -// } + .showIf(isExpanded) - // This geometry reader is somewhat of a workaround. It's "smart" in that it takes up as much - // space as it can and thereby helps the view to understand the maximum allowed height when - // placed in a UIKit context. If ConnectionView would ever be placed as a subview of SwiftUI - // parent, this reader could probably be removed. - GeometryReader { _ in - ScrollView { - VStack(spacing: 16) { - if !indicatorsViewModel.chips.isEmpty { - FeatureIndicatorsView( - viewModel: indicatorsViewModel, - isExpanded: $isExpanded - ) - } + ScrollView { + VStack(spacing: 16) { + FeatureIndicatorsView( + viewModel: indicatorsViewModel, + isExpanded: $isExpanded + ) + .showIf(!indicatorsViewModel.chips.isEmpty) - if isExpanded { - DetailsView(viewModel: viewModel) - .transition(.move(edge: .bottom)) - } - } - .sizeOfView { scrollViewHeight = $0.height } + DetailsView(viewModel: connectionViewModel) + .showIf(isExpanded) } + .sizeOfView { scrollViewHeight = $0.height } } .frame(maxHeight: scrollViewHeight) } @@ -53,7 +42,7 @@ extension ConnectionView { #Preview { ConnectionViewComponentPreview(showIndicators: true, isExpanded: true) { indicatorModel, viewModel, isExpanded in ConnectionView.DetailsContainer( - viewModel: viewModel, + connectionViewModel: viewModel, indicatorsViewModel: indicatorModel, isExpanded: isExpanded ) diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/HeaderView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/HeaderView.swift index 5ec0eb9316cb..24c8358b0b28 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/HeaderView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/ConnectionView/HeaderView.swift @@ -38,23 +38,19 @@ extension ConnectionView { } .accessibilityLabel(viewModel.localizedAccessibilityLabelForSecureLabel) - if viewModel.showConnectionDetails { + Group { Spacer() Image(.iconChevron) .renderingMode(.template) .rotationEffect(isExpanded ? .degrees(-90) : .degrees(90)) .foregroundStyle(.white) - .transaction { transaction in - transaction.animation = nil - } + .accessibilityIdentifier(AccessibilityIdentifier.relayStatusCollapseButton.asString) } + .showIf(viewModel.showConnectionDetails) } - .accessibilityIdentifier(AccessibilityIdentifier.relayStatusCollapseButton.asString) .contentShape(Rectangle()) .onTapGesture { -// withAnimation { isExpanded.toggle() -// } } } } diff --git a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift index b1a369f99eb0..997ad1bd3137 100644 --- a/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift +++ b/ios/MullvadVPN/View controllers/Tunnel/FeatureIndicators/FeatureIndicatorsView.swift @@ -14,12 +14,11 @@ struct FeatureIndicatorsView: View where ViewModel: ChipViewModelProt var body: some View { VStack(alignment: .leading, spacing: 0) { - if isExpanded { - Text(LocalizedStringKey("Active features")) - .font(.footnote.weight(.semibold)) - .foregroundStyle(UIColor.primaryTextColor.color.opacity(0.6)) - .padding(.bottom, 8) - } + Text(LocalizedStringKey("Active features")) + .font(.footnote.weight(.semibold)) + .foregroundStyle(UIColor.primaryTextColor.color.opacity(0.6)) + .padding(.bottom, 8) + .showIf(isExpanded) ChipContainerView(viewModel: viewModel, isExpanded: $isExpanded) } diff --git a/ios/MullvadVPN/Views/MainButtonStyle.swift b/ios/MullvadVPN/Views/MainButtonStyle.swift index ceabd7761b10..e13758a15537 100644 --- a/ios/MullvadVPN/Views/MainButtonStyle.swift +++ b/ios/MullvadVPN/Views/MainButtonStyle.swift @@ -10,12 +10,10 @@ import SwiftUI struct MainButtonStyle: ButtonStyle { var style: Style - var disabled: Bool @Environment(\.isEnabled) private var isEnabled: Bool - init(_ style: Style, disabled: Bool = false) { + init(_ style: Style) { self.style = style - self.disabled = disabled } func makeBody(configuration: Configuration) -> some View { diff --git a/ios/MullvadVPN/Views/SplitMainButton.swift b/ios/MullvadVPN/Views/SplitMainButton.swift index 6f36b839befe..72a879893547 100644 --- a/ios/MullvadVPN/Views/SplitMainButton.swift +++ b/ios/MullvadVPN/Views/SplitMainButton.swift @@ -12,6 +12,7 @@ struct SplitMainButton: View { var text: LocalizedStringKey var image: ImageResource var style: MainButtonStyle.Style + var accessibilityId: AccessibilityIdentifier? @State private var secondaryButtonWidth: CGFloat = 0 @@ -28,6 +29,10 @@ struct SplitMainButton: View { } .padding(.trailing, -secondaryButtonWidth) }) + .ifLet(accessibilityId) { view, value in + view.accessibilityIdentifier(value.asString) + } + Button(action: secondaryAction, label: { Image(image) .resizable()