I have a SwiftUI app with a List displaying an array of model objects. When the user taps a list item we see its detail view.
I want to add previous and next buttons to my detail view, but I'm not sure what needs to happen when previous/next are tapped. (see code below for what I'm looking to do)
My first thought is to make the model variable in the DetailView be a binding, but I'm not sure how this would tie in with the NavigationLink 'stuff'
any/all suggestions appreciated.
thanks!
class Model: Identifiable {
var modelValue: Int
init(modelValue: Int) {
self.modelValue = modelValue
}
static let testData = [Model(modelValue: 3), Model(modelValue: 7), Model(modelValue: 31)]
}
class ModelManager {
static let shared = ModelManager()
let modelList = Model.testData
func previous(for model: Model) - Model? {
if let index = modelList.firstIndex(where: {$0.modelValue == model.modelValue}) {
if index 0 {
return modelList[index - 1]
}
}
return nil
}
func next(for model: Model) - Model? {
if let index = modelList.firstIndex(where: {$0.modelValue == model.modelValue}) {
if index modelList.count - 1 {
return modelList[index + 1]
}
}
return nil
}
}
struct ContentView: View {
let manager:ModelManager = ModelManager.shared
var body: some View {
NavigationView {
List(manager.modelList) { object in
NavigationLink(
destination: DetailView(model: object, previous: manager.previous(for: object), next: manager.next(for: object)),
label: {
Text("fred \(object.modelValue)")
})
}
}
}
}
struct DetailView: View {
var model: Model
var previous: Model?
var next: Model?
var body: some View {
VStack {
HStack {
if previous != nil {
Button("Previous") {
// goto previous
}
}
Spacer()
if next != nil {
Button("Next") {
// goto next
}
}
}
Text("value: \(model.modelValue)")
Spacer()
}
}
}
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Hello folks,
I'm attempting to implement some swiftUI UI code to support filtering of a list.
One part of the filtering involves displaying one checkbox for each case/value of an enum (TangleType below)
TangleFilter is a model class that includes an array of TangleTypeFilter objects (each owning a single bool value and a binding)
Expected behaviour: when user taps a checkbox, the checkbox toggles the display and the filter model object toggles its value.
Actual behaviour: the model is updating appropriately, however the UI is not updating. (the single filter below the list does in fact behave correctly
any and all guidance greatly appreciated
Mike
struct ContentView: View {
@State var isChecked: Bool = false
@ObservedObject var filter = TangleFilter()
@ObservedObject var singleFilter: TangleTypeFilter
init() {
self.singleFilter = TangleTypeFilter(tangleType: .grid)
}
var body: some View {
VStack{
List(filter.tangleTypes, id: \.self) {tangleTypeFilter in
HStack {
// when uncommented the following line returns the following
// compile error:
// Use of unresolved identifier '$tangleTypeFilter'
// CheckBox(isChecked: $tangleTypeFilter.isChecked)
CheckBox(isChecked: tangleTypeFilter.binding)
Text("checked? \(tangleTypeFilter.isChecked.description)")
}
}
CheckBox(isChecked: $singleFilter.isChecked)
}
}
}
struct CheckBox: View {
@Binding var isChecked: Bool {
didSet {
print("setting isChecked: \(isChecked)")
}
}
var imageName: String {
return isChecked ? "checkmark.square" : "square"
}
var body: some View {
Button(action: {
self.isChecked.toggle()
}) {
Image(systemName: self.imageName)
}
}
}
enum TangleType: String, Codable, CaseIterable {
static let filterArray: [TangleTypeFilter] = {
var result: [TangleTypeFilter] = []
for tangleType in TangleType.allCases {
result.append(TangleTypeFilter(tangleType: tangleType))
}
return result
}()
case grid
case row
}
class TangleFilter: ObservableObject {
@Published var tangleTypes: [TangleTypeFilter] = TangleType.filterArray
}
class TangleTypeFilter: ObservableObject {
var tangleType: TangleType
@Published var isChecked: Bool
lazy var binding: Binding<Bool> = Binding(get: {
return self.isChecked
}, set: {
self.isChecked = $0
})
init(tangleType: TangleType) {
self.tangleType = tangleType
self.isChecked = false
}
}
extension TangleTypeFilter: Hashable {
static func == (lhs: TangleTypeFilter, rhs: TangleTypeFilter) -> Bool {
return lhs.tangleType == rhs.tangleType
}
func hash(into hasher: inout Hasher) {
hasher.combine(tangleType)
}
}
I seem to recall hearing that DateFormatters are (or were) expensive to instantiate.
With this in mind, I tried a small experiment with the following code:
class MyClass {
		static let df = DrawingCellView.dateFormatter
static var dateFormatter: DateFormatter {
let result = DateFormatter()
result.dateFormat = "yyyy-MM-dd"
return result
}
		func dateText() -> String {
				return MyClass.dateFormatter.string(from: Date())
		}
When I put a breakpoint in the dateFormatter code, it fires each time I use it. However if I instead use df, the breakpoint only fires once.
Does this make sense?
If so, is this the recommended way to construct a runOnce static constant declaration/assignment?
thanks!
Mike