Add ViewFrameReader

This commit is contained in:
David Langley
2021-08-25 19:24:33 +01:00
parent af477d0e09
commit 32610816e5
10 changed files with 97 additions and 72 deletions
@@ -23,11 +23,12 @@ import SwiftUI
*/
@available(iOS 14.0, *)
struct BorderedInputFieldStyle: TextFieldStyle {
@Environment(\.theme) var theme: Theme
@Environment(\.isEnabled) var isEnabled: Bool
var isEditing: Bool = false
var isError: Bool = false
var isEnabled: Bool = true
private var borderColor: Color {
if !isEnabled {
@@ -93,7 +94,8 @@ struct BorderedInputFieldStyle_Previews: PreviewProvider {
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(BorderedInputFieldStyle(isEditing: true))
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(BorderedInputFieldStyle(isEnabled: false))
.textFieldStyle(BorderedInputFieldStyle())
.disabled(true)
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(BorderedInputFieldStyle(isEditing: true, isError: true))
}
@@ -108,7 +110,8 @@ struct BorderedInputFieldStyle_Previews: PreviewProvider {
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(BorderedInputFieldStyle(isEditing: true))
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(BorderedInputFieldStyle(isEnabled: false))
.textFieldStyle(BorderedInputFieldStyle())
.disabled(true)
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(BorderedInputFieldStyle(isEditing: true, isError: true))
}
@@ -25,7 +25,7 @@ struct Chip: View {
@Environment(\.isEnabled) var isEnabled
@Environment(\.theme) var theme: Theme
let chip: String
let title: String
let onDelete: () -> Void
var backgroundColor: Color {
@@ -44,12 +44,13 @@ struct Chip: View {
var body: some View {
HStack {
Text(chip)
Text(title)
.font(Font(theme.fonts.body))
.lineLimit(1)
Image(systemName: "xmark.circle.fill")
.frame(width: 16, height: 16, alignment: .center)
.onTapGesture(perform: onDelete)
Button(action: onDelete) {
Image(systemName: "xmark.circle.fill")
.frame(width: 16, height: 16, alignment: .center)
}
}
.padding(.leading, 12)
.padding(.top, 6)
@@ -66,8 +67,8 @@ struct Chip: View {
struct Chip_Previews: PreviewProvider {
static var previews: some View {
Group {
Chip(chip: "My great chip", onDelete: { })
Chip(chip: "My great chip", onDelete: { })
Chip(title: "My great chip", onDelete: { })
Chip(title: "My great chip", onDelete: { })
.theme(.dark)
}
}
@@ -22,12 +22,12 @@ import SwiftUI
@available(iOS 14.0, *)
struct Chips: View {
@State private var totalHeight: CGFloat = 0
@State private var frame: CGRect = CGRect.zero
var chips: [String]
var didDeleteChip: (String) -> Void
var verticalSpacing: CGFloat = 16
var horizontalSpacing: CGFloat = 12
let titles: [String]
let didDeleteChip: (String) -> Void
let verticalSpacing: CGFloat = 16
let horizontalSpacing: CGFloat = 12
var body: some View {
Group {
@@ -36,8 +36,8 @@ struct Chips: View {
var y = CGFloat.zero
GeometryReader { geo in
ZStack(alignment: .topLeading, content: {
ForEach(chips, id: \.self) { chip in
Chip(chip: chip) {
ForEach(titles, id: \.self) { chip in
Chip(title: chip) {
didDeleteChip(chip)
}
.alignmentGuide(.leading) { dimension in
@@ -50,7 +50,7 @@ struct Chips: View {
let result = x
if chip == chips.last {
if chip == titles.last {
// Reset x if it's the last.
x = 0
} else {
@@ -62,30 +62,17 @@ struct Chips: View {
.alignmentGuide(.top) { dimension in
// Use next y value and reset if its the last.
let result = y
if chip == chips.last {
if chip == titles.last {
y = 0
}
return result
}
}
})
.background(viewHeightReader($totalHeight))
.background(ViewFrameReader(frame: $frame))
}
}
.frame(height: totalHeight)
}
}
/**
As the flow layout uses a `ZStack` and alignmentGuides to overlay the chips we need to use
Geometry to report back the calculated size
*/
private func viewHeightReader(_ binding: Binding<CGFloat>) -> some View {
return GeometryReader { geo -> Color in
DispatchQueue.main.async {
binding.wrappedValue = geo.frame(in: .local).size.height
}
return .clear
.frame(height: frame.size.height)
}
}
}
@@ -95,8 +82,8 @@ struct Chips_Previews: PreviewProvider {
static var chips: [String] = ["Chip1", "Chip2", "Chip3", "Chip4", "Chip5", "Chip6"]
static var previews: some View {
Group {
Chips(chips: chips, didDeleteChip: { _ in })
Chips(chips: chips, didDeleteChip: { _ in })
Chips(titles: chips, didDeleteChip: { _ in })
Chips(titles: chips, didDeleteChip: { _ in })
.theme(.dark)
}
@@ -28,28 +28,25 @@ struct ChipsInput: View {
@Environment(\.isEnabled) var isEnabled
@State private var chipText: String = ""
@State private var isEditing: Bool = false
var chips: [String]
let titles: [String]
let didAddChip: (String) -> Void
let didDeleteChip: (String) -> Void
var placeholder: String = ""
var didAddChip: (String) -> Void
var didDeleteChip: (String) -> Void
var body: some View {
VStack(spacing: 16) {
TextField(placeholder, text: $chipText) { editing in
isEditing = editing
} onCommit: {
TextField(placeholder, text: $chipText, onCommit: {
didAddChip(chipText)
chipText = ""
}
})
.disabled(!isEnabled)
.disableAutocorrection(true)
.autocapitalization(.none)
.textFieldStyle(FormInputFieldStyle())
Chips(chips: chips, didDeleteChip: didDeleteChip)
Chips(titles: titles, didDeleteChip: didDeleteChip)
.padding(.horizontal)
}
}
@@ -59,7 +56,7 @@ struct ChipsInput: View {
struct ChipsInput_Previews: PreviewProvider {
static var chips = Set<String>(["Website", "Element", "Design", "Matrix/Element"])
static var previews: some View {
ChipsInput(chips: Array(chips)) { chip in
ChipsInput(titles: Array(chips)) { chip in
chips.insert(chip)
} didDeleteChip: { chip in
chips.remove(chip)
@@ -22,10 +22,9 @@ import SwiftUI
*/
@available(iOS 14.0, *)
struct FormInputFieldStyle: TextFieldStyle {
@Environment(\.theme) var theme: Theme
var isEditing: Bool = false
var isEnabled: Bool = true
@Environment(\.theme) var theme: Theme
@Environment(\.isEnabled) var isEnabled
private var textColor: Color {
if !isEnabled {
@@ -59,28 +58,22 @@ struct FormInputFieldStyle_Previews: PreviewProvider {
VectorForm {
TextField("Placeholder", text: .constant(""))
.textFieldStyle(FormInputFieldStyle())
TextField("Placeholder", text: .constant(""))
.textFieldStyle(FormInputFieldStyle(isEditing: true))
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(FormInputFieldStyle())
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(FormInputFieldStyle(isEditing: true))
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(FormInputFieldStyle(isEnabled: false))
.textFieldStyle(FormInputFieldStyle())
.disabled(true)
}
.padding()
VectorForm {
TextField("Placeholder", text: .constant(""))
.textFieldStyle(FormInputFieldStyle())
TextField("Placeholder", text: .constant(""))
.textFieldStyle(FormInputFieldStyle(isEditing: true))
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(FormInputFieldStyle())
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(FormInputFieldStyle(isEditing: true))
TextField("Placeholder", text: .constant("Web"))
.textFieldStyle(FormInputFieldStyle(isEnabled: false))
.textFieldStyle(FormInputFieldStyle())
.disabled(true)
}
.padding()
@@ -36,7 +36,7 @@ struct NotificationSettings<BottomSection: View>: View {
ForEach(viewModel.viewState.ruleIds) { ruleId in
let checked = viewModel.viewState.selectionState[ruleId] ?? false
FormPickerItem(title: ruleId.title, selected: checked) {
viewModel.check(ruleID: ruleId, checked: !checked)
viewModel.update(ruleID: ruleId, isChecked: !checked)
}
}
}
@@ -24,10 +24,10 @@ struct NotificationSettingsKeywords: View {
@ObservedObject var viewModel: NotificationSettingsViewModel
var body: some View {
ChipsInput(
chips: viewModel.viewState.keywords,
placeholder: VectorL10n.settingsNewKeyword,
titles: viewModel.viewState.keywords,
didAddChip: viewModel.add(keyword:),
didDeleteChip: viewModel.remove(keyword:)
didDeleteChip: viewModel.remove(keyword:),
placeholder: VectorL10n.settingsNewKeyword
)
.disabled(!(viewModel.viewState.selectionState[.keywords] ?? false))