diff --git a/CrowdinExport/en.xliff b/CrowdinExport/en.xliff
index 0d942d38..c1c225ca 100644
--- a/CrowdinExport/en.xliff
+++ b/CrowdinExport/en.xliff
@@ -47,11 +47,6 @@
Delete All
No comment provided by engineer.
-
- Edit Phrases
- Edit Phrases
- Edit Phrases
-
No Entries
No Entries
@@ -112,11 +107,21 @@
Are you sure you want to delete? Removed phrases cannot be restored.
Delete phrase confirmation alert title
+
+ Edit Phrases
+ Edit Phrases
+ Edit Phrases button label within the category detail screen
+
Remove Category
Remove Category
Remove category button label within the category detail screen.
+
+ Rename Category
+ Rename Category
+ Rename Category button label within the category detail screen
+
Show Category
Show Category
@@ -255,6 +260,11 @@
Vocable failed to reset. Please try again or reinstall Vocable if the issue persists.
Reset app settings failure alert
+
+ OK
+ OK
+ Button dismissing the alert informing the user that Vocable's application settings failed to reset
+
Vocable has been reset successfully
Vocable has been reset successfully
@@ -263,8 +273,7 @@
OK
OK
- Button dismissing the alert informing the user that Vocable's application settings failed to reset
- Button dismissing the alert informing the user that Vocable's application settings were successfully reset
+ Button dismissing the alert informing the user that Vocable's application settings were successfully reset
You're about to be taken outside of the Vocable app. You may lose head tracking control.
diff --git a/Vocable.xcodeproj/project.pbxproj b/Vocable.xcodeproj/project.pbxproj
index eb0cc902..afdf0188 100644
--- a/Vocable.xcodeproj/project.pbxproj
+++ b/Vocable.xcodeproj/project.pbxproj
@@ -35,13 +35,7 @@
64A220BE2452173C0014EA80 /* Root.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 64A220BD2452173C0014EA80 /* Root.storyboard */; };
64A54545242A490700218BE8 /* GazeEatingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A54544242A490700218BE8 /* GazeEatingView.swift */; };
64B32588254B550600023566 /* EditCategoryDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B32587254B550500023566 /* EditCategoryDetailViewController.swift */; };
- 64B32593254B562B00023566 /* EditCategoryDetailTitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B32592254B562B00023566 /* EditCategoryDetailTitleCollectionViewCell.swift */; };
- 64B3259B254B591D00023566 /* EditCategoryRemoveCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B32597254B591C00023566 /* EditCategoryRemoveCollectionViewCell.swift */; };
- 64B3259C254B591D00023566 /* EditCategoryToggleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B32598254B591D00023566 /* EditCategoryToggleCollectionViewCell.swift */; };
- 64B3259D254B591D00023566 /* EditCategoryRemoveCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 64B32599254B591D00023566 /* EditCategoryRemoveCollectionViewCell.xib */; };
- 64B3259E254B591D00023566 /* EditCategoryToggleCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 64B3259A254B591D00023566 /* EditCategoryToggleCollectionViewCell.xib */; };
64D04A58242951C00006962D /* GazeableAlertPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D04A57242951C00006962D /* GazeableAlertPresentationController.swift */; };
- 64E9CD60258D35B400A2DC1F /* EditCategoryDetailsHeaderCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 64E9CD5F258D35B400A2DC1F /* EditCategoryDetailsHeaderCollectionViewCell.xib */; };
64F49906245B40BC00348592 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B005AE6245B21ED004919F4 /* SettingsViewController.swift */; };
64F8A5FA245CC9FA00B7C0F9 /* EditPhrasesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE7E9892459F7C3007B01F2 /* EditPhrasesViewController.swift */; };
6B005AE3245B057E004919F4 /* VocableCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B005AE2245B057E004919F4 /* VocableCollectionViewController.swift */; };
@@ -159,9 +153,6 @@
A99AF23323FDA8B600BE1184 /* SuggestionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A99AF23223FDA8B600BE1184 /* SuggestionCollectionViewCell.swift */; };
A99AF23523FDA8DC00BE1184 /* SuggestionCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A99AF23423FDA8DC00BE1184 /* SuggestionCollectionViewCell.xib */; };
A9B32C8C240EB6250084E151 /* KeyboardModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B32C8B240EB6250084E151 /* KeyboardModel.swift */; };
- A9B696052433C422004E6960 /* EditCategoriesDefaultCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9B696042433C422004E6960 /* EditCategoriesDefaultCollectionViewCell.xib */; };
- A9B6960B2433CCFD004E6960 /* EditCategoriesDefaultCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B6960A2433CCFD004E6960 /* EditCategoriesDefaultCollectionViewCell.swift */; };
- A9B6960D2433CD30004E6960 /* EditCategoriesCompactCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9B6960C2433CD30004E6960 /* EditCategoriesCompactCollectionViewCell.xib */; };
A9B696112433CF31004E6960 /* EditCategoriesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B696102433CF31004E6960 /* EditCategoriesViewController.swift */; };
A9C32CFF242271F3000EC6F7 /* Phrase+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C32CFE242271F3000EC6F7 /* Phrase+Helpers.swift */; };
A9CF2AEF242D44EE005633A7 /* TimingSensitivityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9CF2AEE242D44EE005633A7 /* TimingSensitivityViewController.swift */; };
@@ -243,13 +234,7 @@
64A220BD2452173C0014EA80 /* Root.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Root.storyboard; sourceTree = ""; };
64A54544242A490700218BE8 /* GazeEatingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GazeEatingView.swift; sourceTree = ""; };
64B32587254B550500023566 /* EditCategoryDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditCategoryDetailViewController.swift; sourceTree = ""; };
- 64B32592254B562B00023566 /* EditCategoryDetailTitleCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditCategoryDetailTitleCollectionViewCell.swift; sourceTree = ""; };
- 64B32597254B591C00023566 /* EditCategoryRemoveCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditCategoryRemoveCollectionViewCell.swift; sourceTree = ""; };
- 64B32598254B591D00023566 /* EditCategoryToggleCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditCategoryToggleCollectionViewCell.swift; sourceTree = ""; };
- 64B32599254B591D00023566 /* EditCategoryRemoveCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EditCategoryRemoveCollectionViewCell.xib; sourceTree = ""; };
- 64B3259A254B591D00023566 /* EditCategoryToggleCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EditCategoryToggleCollectionViewCell.xib; sourceTree = ""; };
64D04A57242951C00006962D /* GazeableAlertPresentationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GazeableAlertPresentationController.swift; sourceTree = ""; };
- 64E9CD5F258D35B400A2DC1F /* EditCategoryDetailsHeaderCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EditCategoryDetailsHeaderCollectionViewCell.xib; sourceTree = ""; };
6B005AE2245B057E004919F4 /* VocableCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VocableCollectionViewController.swift; sourceTree = ""; };
6B005AE4245B06F3004919F4 /* VocableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VocableViewController.swift; sourceTree = ""; };
6B005AE6245B21ED004919F4 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; };
@@ -394,9 +379,6 @@
A99AF23223FDA8B600BE1184 /* SuggestionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionCollectionViewCell.swift; sourceTree = ""; };
A99AF23423FDA8DC00BE1184 /* SuggestionCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SuggestionCollectionViewCell.xib; sourceTree = ""; };
A9B32C8B240EB6250084E151 /* KeyboardModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardModel.swift; sourceTree = ""; };
- A9B696042433C422004E6960 /* EditCategoriesDefaultCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditCategoriesDefaultCollectionViewCell.xib; sourceTree = ""; };
- A9B6960A2433CCFD004E6960 /* EditCategoriesDefaultCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditCategoriesDefaultCollectionViewCell.swift; sourceTree = ""; };
- A9B6960C2433CD30004E6960 /* EditCategoriesCompactCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EditCategoriesCompactCollectionViewCell.xib; sourceTree = ""; };
A9B696102433CF31004E6960 /* EditCategoriesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCategoriesViewController.swift; sourceTree = ""; };
A9C32CFE242271F3000EC6F7 /* Phrase+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Phrase+Helpers.swift"; sourceTree = ""; };
A9CF2AEE242D44EE005633A7 /* TimingSensitivityViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimingSensitivityViewController.swift; sourceTree = ""; };
@@ -597,22 +579,6 @@
path = Root;
sourceTree = "";
};
- 64E9CD67258D3E5500A2DC1F /* Cells */ = {
- isa = PBXGroup;
- children = (
- A9B6960C2433CD30004E6960 /* EditCategoriesCompactCollectionViewCell.xib */,
- A9B6960A2433CCFD004E6960 /* EditCategoriesDefaultCollectionViewCell.swift */,
- A9B696042433C422004E6960 /* EditCategoriesDefaultCollectionViewCell.xib */,
- 64E9CD5F258D35B400A2DC1F /* EditCategoryDetailsHeaderCollectionViewCell.xib */,
- 64B32592254B562B00023566 /* EditCategoryDetailTitleCollectionViewCell.swift */,
- 64B32597254B591C00023566 /* EditCategoryRemoveCollectionViewCell.swift */,
- 64B32599254B591D00023566 /* EditCategoryRemoveCollectionViewCell.xib */,
- 64B32598254B591D00023566 /* EditCategoryToggleCollectionViewCell.swift */,
- 64B3259A254B591D00023566 /* EditCategoryToggleCollectionViewCell.xib */,
- );
- name = Cells;
- sourceTree = "";
- };
6B05020527F3516F000CFE5A /* CategoryReordering */ = {
isa = PBXGroup;
children = (
@@ -788,7 +754,6 @@
children = (
A9B696102433CF31004E6960 /* EditCategoriesViewController.swift */,
64B32587254B550500023566 /* EditCategoryDetailViewController.swift */,
- 64E9CD67258D3E5500A2DC1F /* Cells */,
);
path = EditCategories;
sourceTree = "";
@@ -1060,21 +1025,17 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 64E9CD60258D35B400A2DC1F /* EditCategoryDetailsHeaderCollectionViewCell.xib in Resources */,
A9CF2AF5242D4EF1005633A7 /* SensitivityCollectionViewCell.xib in Resources */,
718D7FB0241954C400FDEF93 /* EditPhrasesCollectionViewCell.xib in Resources */,
71DBE5EB240DE4D000BA8D01 /* WarningView.xib in Resources */,
A99AF23523FDA8DC00BE1184 /* SuggestionCollectionViewCell.xib in Resources */,
A9299C7723F5FC6F00903AB6 /* KeyboardKeyCollectionViewCell.xib in Resources */,
A96D61D724229F7400577144 /* SettingsCollectionViewCell.xib in Resources */,
- 64B3259D254B591D00023566 /* EditCategoryRemoveCollectionViewCell.xib in Resources */,
71B856442416CEB600CB163B /* ToastView.xib in Resources */,
A98E8A2523F1DBF200BF1F22 /* SettingsFooterCollectionViewCell.xib in Resources */,
6B4F3C5E25A5132D008A3CB2 /* Listening.wav in Resources */,
6B724A4E25E046BA0033891F /* SettingsFooterTextSupplementaryView.xib in Resources */,
274540282460DD0F0056EF98 /* Presets.strings in Resources */,
- A9B696052433C422004E6960 /* EditCategoriesDefaultCollectionViewCell.xib in Resources */,
- A9B6960D2433CD30004E6960 /* EditCategoriesCompactCollectionViewCell.xib in Resources */,
30B979B82425583E00309D7C /* InfoPlist.strings in Resources */,
A9F56FC824365BE8008162B6 /* SpeakFunctionKeyboardKeyCollectionViewCell.xib in Resources */,
130C00C420D2AD2C007C3163 /* LaunchScreen.storyboard in Resources */,
@@ -1082,7 +1043,6 @@
6B9DFA6923E88A1B0037673E /* GazeLib.scnassets in Resources */,
6BE23D562444DCBE0032CAE4 /* Localizable.strings in Resources */,
130C00C120D2AD2C007C3163 /* Assets.xcassets in Resources */,
- 64B3259E254B591D00023566 /* EditCategoryToggleCollectionViewCell.xib in Resources */,
2745402E2460DD760056EF98 /* presets.json in Resources */,
64A220BE2452173C0014EA80 /* Root.storyboard in Resources */,
2631B366251181C400B94402 /* GoogleService-Info.plist in Resources */,
@@ -1189,9 +1149,7 @@
64A220BB2452171C0014EA80 /* RootViewController.swift in Sources */,
6B17CD8023EA045D0050BCB8 /* ViewControllerWrapperView.swift in Sources */,
B8DA9DF223F30BAF00FEBE19 /* Cell+NibLoading.swift in Sources */,
- 64B3259C254B591D00023566 /* EditCategoryToggleCollectionViewCell.swift in Sources */,
6BB56BA82422AC2500EA787E /* ToastView.swift in Sources */,
- 64B3259B254B591D00023566 /* EditCategoryRemoveCollectionViewCell.swift in Sources */,
A9DFCC20242D020100136136 /* PublishedValue.swift in Sources */,
6B5C73A727DFE61600004713 /* PredicateBuilders.swift in Sources */,
A9C32CFF242271F3000EC6F7 /* Phrase+Helpers.swift in Sources */,
@@ -1214,7 +1172,6 @@
6B9DFA7623E8D59A0037673E /* UICollectionView+Gaze.swift in Sources */,
6B9DFA7023E88A630037673E /* InterpolableValues.swift in Sources */,
A9DE16FE23F48FD30094DB64 /* TextSuggestionController.swift in Sources */,
- A9B6960B2433CCFD004E6960 /* EditCategoriesDefaultCollectionViewCell.swift in Sources */,
A91030B227E3BF6600281C97 /* VocableListCellContentView.swift in Sources */,
6BE7E9882459DFD5007B01F2 /* VocableNavigationBar.swift in Sources */,
6B5C490D245CB92700A4433C /* CategoryDetailViewController.swift in Sources */,
@@ -1246,7 +1203,6 @@
6B823BCB23F4ADE30099F65E /* Pulse.swift in Sources */,
6B5C49112460AEF900A4433C /* RootEditTextViewController.swift in Sources */,
A9CF2AEF242D44EE005633A7 /* TimingSensitivityViewController.swift in Sources */,
- 64B32593254B562B00023566 /* EditCategoryDetailTitleCollectionViewCell.swift in Sources */,
6459943E25895D9C0010EEFF /* SpeechRecognitionController.swift in Sources */,
4E014DAA27EA53BC00561FC8 /* NSRange+.swift in Sources */,
B8DA9DF423F30BD200FEBE19 /* VectorUtils.swift in Sources */,
diff --git a/Vocable/Common/VocableListCell/VocableListCellContentView.swift b/Vocable/Common/VocableListCell/VocableListCellContentView.swift
index 8087441e..32961150 100644
--- a/Vocable/Common/VocableListCell/VocableListCellContentView.swift
+++ b/Vocable/Common/VocableListCell/VocableListCellContentView.swift
@@ -144,11 +144,18 @@ final class VocableListCellContentView: UIView, UIContentView {
}
private func updatePrimaryLabelButton(with configuration: VocableListContentConfiguration?) {
+ primaryLabelButton.contentHorizontalAlignment = configuration?.primaryContentHorizontalAlignment ?? .center
primaryLabelButton.setTrailingAccessory(configuration?.accessory)
primaryLabelButton.setAttributedTitle(configuration?.attributedTitle, for: .normal)
primaryLabelButton.accessibilityLabel = configuration?.accessibilityLabel
primaryLabelButton.accessibilityIdentifier = configuration?.accessibilityIdentifier
primaryLabelButton.addTarget(self, action: #selector(handlePrimaryActionSelection(_:)), for: .primaryActionTriggered)
+ primaryLabelButton.isEnabled = configuration?.isPrimaryActionEnabled ?? true
+
+ if let backgroundColor = configuration?.primaryBackgroundColor {
+ primaryLabelButton.setFillColor(backgroundColor, for: .normal)
+ primaryLabelButton.setFillColor(backgroundColor.disabled(blending: .collectionViewBackgroundColor), for: .disabled)
+ }
}
private func updateLeadingActionAccessoryButtons(with configuration: VocableListContentConfiguration?) {
diff --git a/Vocable/Common/VocableListCell/VocableListCellPrimaryButton.swift b/Vocable/Common/VocableListCell/VocableListCellPrimaryButton.swift
index 53256b43..cd7324f2 100644
--- a/Vocable/Common/VocableListCell/VocableListCellPrimaryButton.swift
+++ b/Vocable/Common/VocableListCell/VocableListCellPrimaryButton.swift
@@ -13,6 +13,17 @@ final class VocableListCellPrimaryButton: GazeableButton {
private var trailingAccessoryViewLayoutGuide = UILayoutGuide()
private(set) var trailingAccessoryView: UIView?
+ override var isEnabled: Bool {
+ didSet {
+ trailingAccessoryView?.alpha = isEnabled ? 1 : 0.5
+ }
+ }
+
+ init() {
+ super.init(frame: .zero)
+ commonInit()
+ }
+
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
diff --git a/Vocable/Common/VocableListCell/VocableListContentConfiguration.swift b/Vocable/Common/VocableListCell/VocableListContentConfiguration.swift
index b2d4b279..b587f845 100644
--- a/Vocable/Common/VocableListCell/VocableListContentConfiguration.swift
+++ b/Vocable/Common/VocableListCell/VocableListContentConfiguration.swift
@@ -47,6 +47,8 @@ struct VocableListContentConfiguration: UIContentConfiguration, Equatable {
var actionsConfiguration: ActionsConfiguration
var accessibilityIdentifier: String?
var accessibilityLabel: String?
+ var primaryBackgroundColor: UIColor = .defaultCellBackgroundColor
+ var primaryContentHorizontalAlignment: UIControl.ContentHorizontalAlignment = .leading
var traitCollectionChangeHandler: TraitCollectionChangeHandler?
@@ -95,6 +97,14 @@ struct VocableListContentConfiguration: UIContentConfiguration, Equatable {
self.accessibilityIdentifier = accessibilityIdentifier
}
+ static func disclosureCellConfiguration(withTitle title: String, action: @escaping () -> Void) -> VocableListContentConfiguration {
+ .init(title: title, accessory: .disclosureIndicator(), primaryAction: action)
+ }
+
+ static func toggleCellConfiguration(withTitle title: String, isOn: Bool, action: @escaping () -> Void) -> VocableListContentConfiguration {
+ .init(title: title, accessory: .toggle(isOn: isOn), primaryAction: action)
+ }
+
func makeContentView() -> UIView & UIContentView {
VocableListCellContentView(configuration: self)
}
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoriesCompactCollectionViewCell.xib b/Vocable/Features/Settings/EditCategories/EditCategoriesCompactCollectionViewCell.xib
deleted file mode 100644
index 87bd832a..00000000
--- a/Vocable/Features/Settings/EditCategories/EditCategoriesCompactCollectionViewCell.xib
+++ /dev/null
@@ -1,110 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoriesDefaultCollectionViewCell.swift b/Vocable/Features/Settings/EditCategories/EditCategoriesDefaultCollectionViewCell.swift
deleted file mode 100644
index a0360664..00000000
--- a/Vocable/Features/Settings/EditCategories/EditCategoriesDefaultCollectionViewCell.swift
+++ /dev/null
@@ -1,59 +0,0 @@
-//
-// EditCategoriesRegularCollectionViewCell.swift
-// Vocable AAC
-//
-// Created by Jesse Morgan on 3/31/20.
-// Copyright © 2020 WillowTree. All rights reserved.
-//
-
-import UIKit
-
-final class EditCategoriesDefaultCollectionViewCell: UICollectionViewCell {
-
- @IBOutlet weak var moveDownButton: GazeableButton!
- @IBOutlet weak var moveUpButton: GazeableButton!
- @IBOutlet weak var showCategoryDetailButton: GazeableButton!
-
- @IBOutlet private weak var categoryNameLabel: UILabel!
- @IBOutlet private weak var topSeparator: UIView!
- @IBOutlet private weak var bottomSeparator: UIView!
-
- var separatorMask: CellSeparatorMask = .both {
- didSet {
- updateSeparatorMask()
- }
- }
-
- var ordinalButtonMask: CellOrdinalButtonMask = .both {
- didSet {
- updateOrdinalButtonMask()
- }
- }
-
- func setup(title: NSMutableAttributedString) {
- categoryNameLabel.attributedText = title
- }
-
- override func awakeFromNib() {
- super.awakeFromNib()
-
- updateSeparatorMask()
- for button in [moveUpButton, moveDownButton, showCategoryDetailButton].compacted() {
- button.backgroundColor = .collectionViewBackgroundColor
- button.setFillColor(.defaultCellBackgroundColor, for: .normal)
- button.setTitleColor(.defaultTextColor, for: .normal)
- button.isOpaque = true
- }
- }
-
- private func updateSeparatorMask() {
- topSeparator?.isHidden = !separatorMask.contains(.top)
- bottomSeparator?.isHidden = !separatorMask.contains(.bottom)
- }
-
- private func updateOrdinalButtonMask() {
- moveUpButton.isEnabled = ordinalButtonMask.contains(.topUpArrow)
- moveDownButton.isEnabled = ordinalButtonMask.contains(.bottomDownArrow)
- }
-
-}
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoriesDefaultCollectionViewCell.xib b/Vocable/Features/Settings/EditCategories/EditCategoriesDefaultCollectionViewCell.xib
deleted file mode 100644
index 5bf35161..00000000
--- a/Vocable/Features/Settings/EditCategories/EditCategoriesDefaultCollectionViewCell.xib
+++ /dev/null
@@ -1,125 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoriesViewController.swift b/Vocable/Features/Settings/EditCategories/EditCategoriesViewController.swift
index 1d8325ac..e16cba14 100644
--- a/Vocable/Features/Settings/EditCategories/EditCategoriesViewController.swift
+++ b/Vocable/Features/Settings/EditCategories/EditCategoriesViewController.swift
@@ -107,6 +107,7 @@ final class EditCategoriesViewController: PagingCarouselViewController, NSFetche
newConfig.actionsConfiguration.size.widthDimension = .fractionalHeight(1.0)
}
}
+
cell.contentConfiguration = config
}
@@ -229,9 +230,7 @@ final class EditCategoriesViewController: PagingCarouselViewController, NSFetche
if category == Category.listeningModeCategory() {
destination = ListeningModeViewController()
} else {
- let viewController = EditCategoryDetailViewController()
- viewController.category = category
- destination = viewController
+ destination = EditCategoryDetailViewController(category)
}
show(destination, sender: nil)
}
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoryDetailTitleCollectionViewCell.swift b/Vocable/Features/Settings/EditCategories/EditCategoryDetailTitleCollectionViewCell.swift
deleted file mode 100644
index 569c0858..00000000
--- a/Vocable/Features/Settings/EditCategories/EditCategoryDetailTitleCollectionViewCell.swift
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// EditCategoryDetailsHeaderCollectionViewCell.swift
-// Vocable AAC
-//
-// Created by Steve Foster on 5/7/20.
-// Copyright © 2020 WillowTree. All rights reserved.
-//
-
-import UIKit
-
-protocol EditCategoryDetailsHeaderCollectionViewCellDelegate: AnyObject {
- func didTapEdit()
-}
-
-protocol EditCategoryDetailTitleCollectionViewCellDelegate: AnyObject {
- func didTapEdit()
-}
-
-final class EditCategoryDetailTitleCollectionViewCell: UICollectionViewCell {
-
- weak var delegate: EditCategoryDetailTitleCollectionViewCellDelegate?
-
- @IBOutlet weak var textLabel: UILabel!
- @IBOutlet weak var editButton: GazeableButton!
-
- override func awakeFromNib() {
- super.awakeFromNib()
-
- editButton.backgroundColor = .collectionViewBackgroundColor
- editButton.setFillColor(.defaultCellBackgroundColor, for: .normal)
- editButton.setTitleColor(.defaultTextColor, for: .normal)
- editButton.isOpaque = true
- editButton.addTarget(self, action: #selector(didTapEdit(_:)), for: .primaryActionTriggered)
- }
-
- @objc func didTapEdit(_ sender: Any) {
- delegate?.didTapEdit()
- }
-
-}
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoryDetailViewController.swift b/Vocable/Features/Settings/EditCategories/EditCategoryDetailViewController.swift
index b86582f3..02d981ba 100644
--- a/Vocable/Features/Settings/EditCategories/EditCategoryDetailViewController.swift
+++ b/Vocable/Features/Settings/EditCategories/EditCategoryDetailViewController.swift
@@ -9,31 +9,43 @@
import UIKit
import CoreData
-final class EditCategoryDetailViewController: VocableCollectionViewController, EditCategoryDetailTitleCollectionViewCellDelegate {
-
- var category: Category!
+final class EditCategoryDetailViewController: VocableCollectionViewController {
+ private typealias DataSource = UICollectionViewDiffableDataSource
+ private typealias Snapshot = NSDiffableDataSourceSnapshot
+ private typealias CellRegistration = UICollectionView.CellRegistration
private enum Section: Int, CaseIterable {
- case header
- case body
+ case editCategory
+ case editPhrase
+ case removeCategory
}
private enum EditCategoryItem: Int {
- case titleEditView
+ case renameCategory
case showCategoryToggle
- case addPhrase
+ case editPhrases
case removeCategory
}
+ let category: Category
+
private let context = NSPersistentContainer.shared.viewContext
- private lazy var dataSource: UICollectionViewDiffableDataSource = .init(collectionView: collectionView) { (collectionView, indexPath, item) -> UICollectionViewCell in
- return self.collectionView(collectionView, cellForItemAt: indexPath, item: item)
+ private var dataSource: DataSource?
+
+ init(_ category: Category) {
+ self.category = category
+ super.init(nibName: nil, bundle: nil)
+ }
+
+ @available(*, unavailable)
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
- assert(category != nil, "Category not provided")
+ dataSource = makeDataSource()
setupNavigationBar()
setupCollectionView()
@@ -41,100 +53,177 @@ final class EditCategoryDetailViewController: VocableCollectionViewController, E
}
private func setupNavigationBar() {
+ navigationBar.title = category.name
navigationBar.leftButton = {
let button = GazeableButton()
button.setImage(UIImage(systemName: "arrow.left"), for: .normal)
- button.addTarget(self, action: #selector(handleBackButton(_:)), for: .primaryActionTriggered)
+ button.addTarget(self, action: #selector(handleBackButton), for: .primaryActionTriggered)
button.accessibilityIdentifier = "navigationBar.backButton"
return button
}()
}
// MARK: UICollectionViewDataSource
+
+ private func makeDataSource() -> DataSource {
+ let renameCategoryRegistration = makeRenameCategoryCellRegistration()
+ let showCategoryRegistration = makeShowCategoryCellRegistration()
+ let editPhrasesRegistration = makeEditPhrasesCellRegistration()
+ let removeCategoryRegistration = makeRemoveCategoryCellRegistration()
+
+ return DataSource(collectionView: collectionView) { (collectionView, indexPath, item) -> UICollectionViewCell? in
+ switch item {
+ case .renameCategory:
+ return collectionView.dequeueConfiguredReusableCell(
+ using: renameCategoryRegistration,
+ for: indexPath,
+ item: item)
+ case .showCategoryToggle:
+ return collectionView.dequeueConfiguredReusableCell(
+ using: showCategoryRegistration,
+ for: indexPath,
+ item: item)
+ case .editPhrases:
+ return collectionView.dequeueConfiguredReusableCell(
+ using: editPhrasesRegistration,
+ for: indexPath,
+ item: item)
+ case .removeCategory:
+ return collectionView.dequeueConfiguredReusableCell(
+ using: removeCategoryRegistration,
+ for: indexPath,
+ item: item)
+ }
+ }
+ }
private func updateDataSource() {
- var snapshot = NSDiffableDataSourceSnapshot()
- snapshot.appendSections([.header])
- snapshot.appendItems([.titleEditView])
+ var snapshot = Snapshot()
- snapshot.appendSections([.body])
- snapshot.appendItems([.showCategoryToggle, .addPhrase, .removeCategory])
+ snapshot.appendSections([.editCategory, .editPhrase, .removeCategory])
+ snapshot.appendItems([.renameCategory, .showCategoryToggle], toSection: .editCategory)
+ snapshot.appendItems([.editPhrases], toSection: .editPhrase)
+ snapshot.appendItems([.removeCategory], toSection: .removeCategory)
- dataSource.apply(snapshot, animatingDifferences: false)
+ dataSource?.apply(snapshot, animatingDifferences: false)
}
-
- private func setupCollectionView() {
- collectionView.register(UINib(nibName: "EditCategoryDetailsHeaderCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: EditCategoryDetailTitleCollectionViewCell.reuseIdentifier)
- collectionView.register(UINib(nibName: "EditCategoryToggleCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: EditCategoryToggleCollectionViewCell.reuseIdentifier)
- collectionView.register(UINib(nibName: "EditCategoryRemoveCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: EditCategoryRemoveCollectionViewCell.reuseIdentifier)
- collectionView.register(UINib(nibName: "SettingsCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: SettingsCollectionViewCell.reuseIdentifier)
- collectionView.backgroundColor = .collectionViewBackgroundColor
- collectionView.collectionViewLayout = UICollectionViewCompositionalLayout { (sectionIndex, environment) -> NSCollectionLayoutSection? in
+ // MARK: Cell Registrations
- let section = self.dataSource.snapshot().sectionIdentifiers[sectionIndex]
- switch section {
- case .header:
- return self.headerSection(environment: environment)
- case .body:
- return self.bodySection(environment: environment)
+ private func makeRenameCategoryCellRegistration() -> CellRegistration {
+ CellRegistration { cell, _, item in
+ guard item == .renameCategory else {
+ return assertionFailure("This cell registration is for the Rename Category cell.")
+ }
+
+ var config: VocableListContentConfiguration = .disclosureCellConfiguration(
+ withTitle: NSLocalizedString(
+ "category_editor.detail.button.rename_category.title",
+ comment: "Rename Category button label within the category detail screen")
+ ) { [weak self] in
+ self?.handleRenameCategory()
}
+
+ config.accessibilityIdentifier = "rename_category_button"
+
+ cell.contentConfiguration = config
}
}
- private func headerSection(environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
+ private func makeShowCategoryCellRegistration() -> CellRegistration {
+ CellRegistration { [category] cell, indexPath, item in
+ guard item == .showCategoryToggle else {
+ return assertionFailure("This cell registration is for the Show Category cell.")
+ }
- let itemHeightDimension = NSCollectionLayoutDimension.absolute(50)
- let itemWidthDimension = NSCollectionLayoutDimension.fractionalWidth(1.0)
+ var config: VocableListContentConfiguration = .toggleCellConfiguration(
+ withTitle: NSLocalizedString(
+ "category_editor.detail.button.show_category.title",
+ comment: "Show category button label within the category detail screen."),
+ isOn: !category.isHidden
+ ) { [weak self] in
+ self?.handleToggle(at: indexPath)
+ }
- let itemSize = NSCollectionLayoutSize(widthDimension: itemWidthDimension, heightDimension: itemHeightDimension)
- let item = NSCollectionLayoutItem(layoutSize: itemSize)
- let groupSize = NSCollectionLayoutSize(widthDimension: itemWidthDimension, heightDimension: itemHeightDimension)
- let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)
- group.interItemSpacing = .fixed(8)
+ config.isPrimaryActionEnabled = category.identifier != .userFavorites
+ config.accessibilityIdentifier = "show_category_toggle"
- let section = NSCollectionLayoutSection(group: group)
- section.interGroupSpacing = 8
- section.contentInsets = sectionInsets(for: environment)
- section.contentInsets.top = 8
- return section
+ cell.contentConfiguration = config
+ }
}
- private func bodySection(environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
+ private func makeEditPhrasesCellRegistration() -> CellRegistration {
+ CellRegistration { [category] cell, _, item in
+ guard item == .editPhrases else {
+ return assertionFailure("This cell registration is for the Edit Phrases cell.")
+ }
- let itemHeightDimension: NSCollectionLayoutDimension
- let itemWidthDimension = NSCollectionLayoutDimension.fractionalWidth(1.0)
+ var config: VocableListContentConfiguration = .disclosureCellConfiguration(
+ withTitle: NSLocalizedString(
+ "category_editor.detail.button.edit_phrases.title",
+ comment: "Edit Phrases button label within the category detail screen")
+ ) { [weak self] in
+ self?.displayEditPhrasesViewController()
+ }
- if sizeClass.contains(any: .compact) {
- itemHeightDimension = .absolute(50)
- } else {
- itemHeightDimension = .absolute(100)
+ config.isPrimaryActionEnabled = category.allowsCustomPhrases
+ config.accessibilityIdentifier = "edit_phrases_cell"
+
+ cell.contentConfiguration = config
}
+ }
- let itemSize = NSCollectionLayoutSize(widthDimension: itemWidthDimension, heightDimension: itemHeightDimension)
- let item = NSCollectionLayoutItem(layoutSize: itemSize)
- let groupSize = NSCollectionLayoutSize(widthDimension: itemWidthDimension, heightDimension: itemHeightDimension)
- let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)
- group.interItemSpacing = .fixed(8)
+ private func makeRemoveCategoryCellRegistration() -> CellRegistration {
+ CellRegistration { [category] cell, _, item in
+ guard item == .removeCategory else {
+ return assertionFailure("This cell registration is for the Remove Category cell.")
+ }
- let section = NSCollectionLayoutSection(group: group)
- section.interGroupSpacing = 8
- section.contentInsets = sectionInsets(for: environment)
- section.contentInsets.top = 8
- return section
+ var config: VocableListContentConfiguration = .init(attributedText: .removeCategoryTitle) { [weak self] in
+ self?.handleRemoveCategory()
+ }
+
+ config.isPrimaryActionEnabled = category.identifier != .userFavorites
+ config.accessibilityIdentifier = "remove_category_cell"
+ config.primaryBackgroundColor = .errorRed
+ config.primaryContentHorizontalAlignment = .center
+ config.traitCollectionChangeHandler = { _, updatedConfig in
+ updatedConfig.attributedTitle = .removeCategoryTitle
+ }
+
+ cell.contentConfiguration = config
+ }
}
+
+ private func setupCollectionView() {
+ collectionView.backgroundColor = .collectionViewBackgroundColor
+ collectionView.delaysContentTouches = false
- private func sectionInsets(for environment: NSCollectionLayoutEnvironment) -> NSDirectionalEdgeInsets {
- return NSDirectionalEdgeInsets(top: 0,
- leading: max(view.layoutMargins.left - environment.container.contentInsets.leading, 0),
- bottom: 0,
- trailing: max(view.layoutMargins.right - environment.container.contentInsets.trailing, 0))
+ collectionView.collectionViewLayout = UICollectionViewCompositionalLayout { [weak self] (sectionIndex, environment) -> NSCollectionLayoutSection? in
+
+ let section = self?.dataSource?.snapshot().sectionIdentifiers[sectionIndex]
+
+ switch section {
+ case .editCategory:
+ return self?.topSection(environment: environment)
+ case .editPhrase, .removeCategory:
+ return self?.defaultSection(environment: environment)
+ case .none:
+ return nil
+ }
+ }
}
private func defaultSection(environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
+ let section = topSection(environment: environment)
+ section.contentInsets.top += (sizeClass == .hCompact_vRegular) ? 32 : 16
+ return section
+ }
+ private func topSection(environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
let itemHeightDimension: NSCollectionLayoutDimension
let itemWidthDimension = NSCollectionLayoutDimension.fractionalWidth(1.0)
+ let columnCount: Int
if sizeClass.contains(any: .compact) {
itemHeightDimension = .absolute(50)
@@ -142,134 +231,60 @@ final class EditCategoryDetailViewController: VocableCollectionViewController, E
itemHeightDimension = .absolute(100)
}
+ if sizeClass == .hCompact_vRegular {
+ columnCount = 1
+ } else {
+ columnCount = 2
+ }
+
let itemSize = NSCollectionLayoutSize(widthDimension: itemWidthDimension, heightDimension: itemHeightDimension)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: itemWidthDimension, heightDimension: itemHeightDimension)
- let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)
+ let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: columnCount)
group.interItemSpacing = .fixed(8)
let section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = 8
section.contentInsets = sectionInsets(for: environment)
- return section
- }
-
- private func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath, item: EditCategoryItem) -> UICollectionViewCell {
- switch item {
- case .titleEditView:
- let cell = collectionView.dequeueReusableCell(withReuseIdentifier: EditCategoryDetailTitleCollectionViewCell.reuseIdentifier, for: indexPath) as! EditCategoryDetailTitleCollectionViewCell
- cell.delegate = self
- cell.textLabel.text = category.name
- cell.editButton.isEnabled = true
-
- // Assign identifiers for automation
- cell.accessibilityIdentifier = "category_title"
- cell.editButton.accessibilityIdentifier = "category_title_edit_button"
-
- return cell
-
- case .showCategoryToggle:
- let cell = collectionView.dequeueReusableCell(withReuseIdentifier: EditCategoryToggleCollectionViewCell.reuseIdentifier, for: indexPath) as! EditCategoryToggleCollectionViewCell
- cell.isEnabled = shouldEnableItem(at: indexPath)
- cell.textLabel.text = NSLocalizedString("category_editor.detail.button.show_category.title", comment: "Show category button label within the category detail screen.")
-
- if let category = category {
- cell.showCategorySwitch.isOn = !category.isHidden
- cell.showCategorySwitch.isEnabled = (category.identifier != .userFavorites)
- }
-
- // Assign an identifier for automation
- cell.showCategorySwitch.accessibilityIdentifier = "show_category_toggle"
-
- return cell
-
- case .addPhrase:
- let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SettingsCollectionViewCell.reuseIdentifier, for: indexPath) as! SettingsCollectionViewCell
- let title = NSLocalizedString("Edit Phrases", comment: "Edit Phrases")
- cell.setup(title: title, image: UIImage(systemName: "chevron.right"))
- cell.isEnabled = shouldEnableItem(at: indexPath)
-
- // Assign an identifier for automation
- cell.accessibilityIdentifier = "edit_phrases_cell"
-
- return cell
-
- case .removeCategory:
- let cell = collectionView.dequeueReusableCell(withReuseIdentifier: EditCategoryRemoveCollectionViewCell.reuseIdentifier, for: indexPath) as! EditCategoryRemoveCollectionViewCell
- cell.isEnabled = shouldEnableItem(at: indexPath)
- cell.textLabel.text = NSLocalizedString("category_editor.detail.button.remove_category.title", comment: "Remove category button label within the category detail screen.")
-
- // Assign an identifier for automation
- cell.accessibilityIdentifier = "remove_category_cell"
-
- return cell
- }
- }
-
- func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
- guard let selectedItem = dataSource.itemIdentifier(for: indexPath) else { return }
- if collectionView.indexPathForGazedItem != indexPath {
- collectionView.deselectItem(at: indexPath, animated: true)
- }
- switch selectedItem {
- case .titleEditView:
- return
- case .showCategoryToggle:
- handleToggle(at: indexPath)
- case .addPhrase:
- displayEditPhrasesViewController()
- case .removeCategory:
- handleRemoveCategory()
- }
- }
-
- func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
- return shouldEnableItem(at: indexPath)
- }
-
- func collectionView(_ collectionView: UICollectionView, shouldhi indexPath: IndexPath) -> Bool {
- return shouldEnableItem(at: indexPath)
- }
+ section.contentInsets.top = 8
- func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
- return shouldEnableItem(at: indexPath)
+ return section
}
- private func shouldEnableItem(at indexPath: IndexPath) -> Bool {
- guard let selectedItem = dataSource.itemIdentifier(for: indexPath) else { return false }
-
- switch selectedItem {
- case .titleEditView:
- return true
- case .showCategoryToggle, .removeCategory:
- return (category.identifier != .userFavorites)
- case .addPhrase:
- return category.allowsCustomPhrases
- }
+ private func sectionInsets(for environment: NSCollectionLayoutEnvironment) -> NSDirectionalEdgeInsets {
+ return NSDirectionalEdgeInsets(top: 0,
+ leading: max(view.layoutMargins.left - environment.container.contentInsets.leading, 0),
+ bottom: 0,
+ trailing: max(view.layoutMargins.right - environment.container.contentInsets.trailing, 0))
}
// MARK: Actions
- @objc func backButtonPressed(_ sender: Any) {
- self.navigationController?.popViewController(animated: true)
- }
-
- @objc func handleBackButton(_ sender: Any) {
+ @objc private func handleBackButton() {
navigationController?.popViewController(animated: true)
}
private func handleToggle(at indexPath: IndexPath) {
- guard let category = category, let cell = collectionView.cellForItem(at: indexPath) as? EditCategoryToggleCollectionViewCell else { return }
-
- let shouldShowCategory = !cell.showCategorySwitch.isOn
+ let shouldShowCategory = !category.isHidden
category.setValue(!category.isHidden, forKey: "isHidden")
- cell.showCategorySwitch.isOn = shouldShowCategory
+
try? Category.updateAllOrdinalValues(in: context)
saveContext()
if category == Category.listeningModeCategory() {
AppConfig.isListeningModeEnabled = shouldShowCategory
}
+
+ // Update the cell's config
+
+ guard let cell = collectionView.cellForItem(at: indexPath),
+ var config = cell.contentConfiguration as? VocableListContentConfiguration,
+ case .toggle(let isOn) = config.accessory?.content else {
+ return
+ }
+
+ config.accessory = .toggle(isOn: !isOn)
+ cell.contentConfiguration = config
}
private func displayEditPhrasesViewController() {
@@ -293,17 +308,17 @@ final class EditCategoryDetailViewController: VocableCollectionViewController, E
}
private func handleRemoveCategory() {
- let title = NSLocalizedString("category_editor.alert.delete_category_confirmation.title", comment: "Remove category alert title")
- let confirmButtonTitle = NSLocalizedString("category_editor.alert.delete_category_confirmation.button.remove.title", comment: "Remove category alert action title")
- let cancelButtonTitle = NSLocalizedString("category_editor.alert.delete_category_confirmation.button.cancel.title", comment: "Cancel alert action title")
- let alert = GazeableAlertViewController(alertTitle: title)
- alert.addAction(GazeableAlertAction(title: confirmButtonTitle, handler: { self.removeCategory() }))
- alert.addAction(GazeableAlertAction(title: cancelButtonTitle, style: .bold, handler: { self.deselectCell() }))
- self.present(alert, animated: true)
+ let alert: GazeableAlertViewController = .removeCategoryAlert { [weak self] in
+ self?.removeCategory()
+ } cancelAction: { [weak self] in
+ self?.deselectCell()
+ }
+
+ present(alert, animated: true)
}
private func removeCategory() {
- guard let category = category else { return }
+
if category.isUserGenerated {
context.delete(category)
} else {
@@ -334,9 +349,7 @@ final class EditCategoryDetailViewController: VocableCollectionViewController, E
return false
}
- // MARK: EditCategoryDetailTitleCollectionViewCellDelegate
-
- func didTapEdit() {
+ func handleRenameCategory() {
guard let categoryIdentifier = category.identifier else {
assertionFailure("Category has no identifier")
return
@@ -345,7 +358,7 @@ final class EditCategoryDetailViewController: VocableCollectionViewController, E
let initialValue = category.name ?? ""
let viewController = EditTextViewController()
viewController.initialText = initialValue
- viewController.editTextCompletionHandler = { (newText) -> Void in
+ viewController.editTextCompletionHandler = { [weak self] newText in
let context = NSPersistentContainer.shared.viewContext
if let category = Category.fetchObject(in: context, matching: categoryIdentifier) {
@@ -362,7 +375,9 @@ final class EditCategoryDetailViewController: VocableCollectionViewController, E
comment: "User edited name of the category and saved it successfully")
ToastWindow.shared.presentEphemeralToast(withTitle: alertMessage)
- self.collectionView.reloadData()
+
+ self?.collectionView.reloadData()
+ self?.navigationBar.title = newText
} catch {
assertionFailure("Failed to save category: \(error)")
}
@@ -372,3 +387,48 @@ final class EditCategoryDetailViewController: VocableCollectionViewController, E
}
}
+
+private extension GazeableAlertViewController {
+ static func removeCategoryAlert(
+ removeAction: @escaping () -> Void,
+ cancelAction: @escaping () -> Void
+ ) -> GazeableAlertViewController {
+ let title = NSLocalizedString("category_editor.alert.delete_category_confirmation.title", comment: "Remove category alert title")
+ let confirmButtonTitle = NSLocalizedString("category_editor.alert.delete_category_confirmation.button.remove.title", comment: "Remove category alert action title")
+ let cancelButtonTitle = NSLocalizedString("category_editor.alert.delete_category_confirmation.button.cancel.title", comment: "Cancel alert action title")
+
+ let alert = GazeableAlertViewController(alertTitle: title)
+ alert.addAction(GazeableAlertAction(title: cancelButtonTitle, handler: cancelAction))
+ alert.addAction(GazeableAlertAction(title: confirmButtonTitle, style: .destructive, handler: removeAction))
+
+ return alert
+ }
+}
+
+private extension NSAttributedString {
+ static var removeCategoryTitle: NSAttributedString {
+ let isRightToLeftLayout = UITraitCollection.current.layoutDirection == .rightToLeft
+
+ let buttonText = NSLocalizedString("category_editor.detail.button.remove_category.title", comment: "Remove category button label within the category detail screen.")
+
+ let formatString: String = isRightToLeftLayout ?
+ .localizedStringWithFormat("%@ ", buttonText) :
+ .localizedStringWithFormat(" %@", buttonText)
+
+ let attributes: [NSAttributedString.Key: Any] = [
+ .font: UIFont.systemFont(ofSize: 22, weight: .bold),
+ .foregroundColor: UIColor.white
+ ]
+
+ let text = NSMutableAttributedString(string: formatString, attributes: attributes)
+ let attachment = NSMutableAttributedString(attachment: NSTextAttachment(image: UIImage(systemName: "trash")!))
+ attachment.addAttributes(attributes, range: .entireRange(of: attachment.string))
+
+ let textRange = NSRange(of: text.string)
+ let insertionIndex = isRightToLeftLayout ? textRange.upperBound : textRange.lowerBound
+
+ text.insert(attachment, at: insertionIndex)
+
+ return text
+ }
+}
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoryDetailsHeaderCollectionViewCell.xib b/Vocable/Features/Settings/EditCategories/EditCategoryDetailsHeaderCollectionViewCell.xib
deleted file mode 100644
index 66f84965..00000000
--- a/Vocable/Features/Settings/EditCategories/EditCategoryDetailsHeaderCollectionViewCell.xib
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoryRemoveCollectionViewCell.swift b/Vocable/Features/Settings/EditCategories/EditCategoryRemoveCollectionViewCell.swift
deleted file mode 100644
index 37e67da3..00000000
--- a/Vocable/Features/Settings/EditCategories/EditCategoryRemoveCollectionViewCell.swift
+++ /dev/null
@@ -1,21 +0,0 @@
-//
-// EditCategoryRemoveCollectionViewCell.swift
-// Vocable AAC
-//
-// Created by Thomas Shealy on 3/31/20.
-// Copyright © 2020 WillowTree. All rights reserved.
-//
-
-import UIKit
-
-final class EditCategoryRemoveCollectionViewCell: VocableCollectionViewCell {
-
- @IBOutlet weak var textLabel: UILabel!
-
- override func updateContentViews() {
- super.updateContentViews()
-
- textLabel?.textColor = isEnabled ? .defaultTextColor : UIColor.defaultTextColor.disabled(blending: borderedView.backgroundColor)
- }
-
-}
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoryRemoveCollectionViewCell.xib b/Vocable/Features/Settings/EditCategories/EditCategoryRemoveCollectionViewCell.xib
deleted file mode 100644
index aa581757..00000000
--- a/Vocable/Features/Settings/EditCategories/EditCategoryRemoveCollectionViewCell.xib
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoryToggleCollectionViewCell.swift b/Vocable/Features/Settings/EditCategories/EditCategoryToggleCollectionViewCell.swift
deleted file mode 100644
index ed4cec43..00000000
--- a/Vocable/Features/Settings/EditCategories/EditCategoryToggleCollectionViewCell.swift
+++ /dev/null
@@ -1,27 +0,0 @@
-//
-// EditCategoryToggleCollectionViewCell.swift
-// Vocable AAC
-//
-// Created by Thomas Shealy on 3/31/20.
-// Copyright © 2020 WillowTree. All rights reserved.
-//
-
-import UIKit
-
-final class EditCategoryToggleCollectionViewCell: VocableCollectionViewCell {
-
- @IBOutlet weak var showCategorySwitch: UISwitch!
- @IBOutlet weak var textLabel: UILabel!
-
- override func awakeFromNib() {
- super.awakeFromNib()
-
- showCategorySwitch.isUserInteractionEnabled = false
- }
-
- override func updateContentViews() {
- super.updateContentViews()
- textLabel?.textColor = isEnabled ? .defaultTextColor : UIColor.defaultTextColor.disabled(blending: borderedView.backgroundColor)
- }
-
-}
diff --git a/Vocable/Features/Settings/EditCategories/EditCategoryToggleCollectionViewCell.xib b/Vocable/Features/Settings/EditCategories/EditCategoryToggleCollectionViewCell.xib
deleted file mode 100644
index 9a8e968d..00000000
--- a/Vocable/Features/Settings/EditCategories/EditCategoryToggleCollectionViewCell.xib
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Vocable/Features/Settings/SettingsViewController.swift b/Vocable/Features/Settings/SettingsViewController.swift
index 1d599a9a..49a48382 100644
--- a/Vocable/Features/Settings/SettingsViewController.swift
+++ b/Vocable/Features/Settings/SettingsViewController.swift
@@ -391,7 +391,7 @@ final class SettingsViewController: VocableCollectionViewController, MFMailCompo
private func presentResetFailureAlert() {
let alertString = NSLocalizedString("settings.alert.reset_app_settings_failure.body",
comment: "body of alert presented when Vocable's application settings failed to reset")
- let dismissTitle = NSLocalizedString("settings.alert.reset_app_settings_success.button.ok",
+ let dismissTitle = NSLocalizedString("settings.alert.reset_app_settings_failure.button.ok",
comment: "Button dismissing the alert informing the user that Vocable's application settings failed to reset")
let alertViewController = GazeableAlertViewController(alertTitle: alertString)
diff --git a/Vocable/Supporting Files/en.lproj/Localizable.strings b/Vocable/Supporting Files/en.lproj/Localizable.strings
index e5bc64ab..7d1e9eb9 100644
--- a/Vocable/Supporting Files/en.lproj/Localizable.strings
+++ b/Vocable/Supporting Files/en.lproj/Localizable.strings
@@ -96,8 +96,12 @@
/* Settings screen header title */
"settings.header.title" = "Settings";
+"category_editor.detail.button.rename_category.title" = "Rename Category";
+
"category_editor.detail.button.show_category.title" = "Show Category";
+"category_editor.detail.button.edit_phrases.title" = "Edit Phrases";
+
"category_editor.detail.button.remove_category.title" = "Remove Category";
"category_editor.toast.successfully_saved.title" = "Changes Saved";
@@ -161,4 +165,4 @@
"settings.alert.reset_app_settings_failure.body" = "Vocable failed to reset. Please try again or reinstall Vocable if the issue persists.";
-"settings.alert.reset_app_settings_success.button.ok" = "OK";
+"settings.alert.reset_app_settings_failure.button.ok" = "OK";
diff --git a/VocableUITests/Screens/CustomCategoriesScreen.swift b/VocableUITests/Screens/CustomCategoriesScreen.swift
index 12e94e8f..c0a16f51 100644
--- a/VocableUITests/Screens/CustomCategoriesScreen.swift
+++ b/VocableUITests/Screens/CustomCategoriesScreen.swift
@@ -15,7 +15,7 @@ class CustomCategoriesScreen: BaseScreen {
let mainScreen = MainScreen()
let categoriesPageAddPhraseButton = XCUIApplication().buttons["settingsCategory.addPhraseButton"]
- let editCategoryPhrasesCell = XCUIApplication().cells["edit_phrases_cell"]
+ let editCategoryPhrasesCell = XCUIApplication().buttons["edit_phrases_cell"]
let categoriesPageEditPhraseButton = XCUIApplication().buttons["categoryPhrase.editButton"]
let categoriesPageDeletePhraseButton = XCUIApplication().buttons["categoryPhrase.deleteButton"]
diff --git a/VocableUITests/Screens/SettingsScreen.swift b/VocableUITests/Screens/SettingsScreen.swift
index 27109fe9..229e22e5 100644
--- a/VocableUITests/Screens/SettingsScreen.swift
+++ b/VocableUITests/Screens/SettingsScreen.swift
@@ -23,7 +23,7 @@ class SettingsScreen: BaseScreen {
let categoryUpButton = "chevron.up"
let categoryDownButton = "chevron.down"
let categoryForwardButton = "Forward"
- let showCategorySwitch = XCUIApplication().switches["show_category_toggle"]
+ let showCategorySwitch = XCUIApplication().buttons["show_category_toggle"]
let hideCategorySwitch = "hide"
let categoryShowButton = "show"
let settingsPageAddCategoryButton = XCUIApplication().buttons["settingsCategory.addCategoryButton"]