Post

Replies

Boosts

Views

Activity

Can I put a swiftUI container's content in a func/var/let?
I'm trying to find a syntactically correct way to put the contents of a Container in a separate variable (or function). Can anyone steer me in the right direction? thanks, in advance, mike struct ContentView: View { var body: some View { VStack(content: containerContent) .padding() } var containerContent: () -> Content { return { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } } }
1
0
463
Nov ’23
Unable to deploy CloudKit Dev Schema to production
This will be the initial production schema for this container. When I attempt to start deployment, the Confirm Deployment dialog appears and spins for a while. It then reports "There was a problem loading the environment's status." When I clear the error the Confirm Deployment dialog reports: "No Changes to Deploy" "The schema in the development environment is the same as production." (spoiler, they are not the same) Any suggestions?
0
0
552
May ’21
In SwiftUI I would like to declare an @ObservedObject var with a type defined in a protocol...
See sample code below... Basically it's a galleryView with a dataSource that can add/remove items dynamically. It works as expected when GalleryView's dataSource variable has a type (that conforms to ObservableObject) However when I change dataSource's type to be a protocol, I can't seem to get my code to compile. Any guidance on how to use a protocol in GalleryView, and continue to keep the UI updating when the model object's item list changes? thanks! Mike protocol GalleryDataSource: ObservableObject {     var itemCount: Int { get }     func item(for index: Int) - String } class GalleryModel: ObservableObject {     static let test1: GalleryModel = GalleryModel(items: ["A","B","C"])     @Published var items: [String]     init(items: [String]) {         self.items = items     } } extension GalleryModel: GalleryDataSource {     var itemCount: Int {         return items.count     }     func item(for index: Int) - String {         return items[index]     } } struct ContentView: View {     var model: GalleryModel = GalleryModel.test1     var body: some View {         VStack {             GalleryView(dataSource: model)             Button("Add Item") {                 model.items.append("\(model.items.count)")             }         }     } } struct GalleryView: View {     @ObservedObject var dataSource: GalleryModel //GalleryDataSource     var body: some View {         ScrollView(.horizontal, content: {             HStack {                 ForEach(0..self.dataSource.itemCount, id:\.self) { index in                     Text(dataSource.item(for: index))                         .padding()                 }             }         })     } }
1
0
4.2k
May ’21
Not sure how to add Prev/Next buttons in my SwiftUI List's Detail view.
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()         }     } }
1
0
1.4k
Mar ’21
Having problems with a SwiftUI checkbox binding I'm implementing...
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)     } }
6
1
2.4k
Jan ’21
A potentially dumb questions about swift (and dateFormatters)
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 { &#9;&#9;static let df = DrawingCellView.dateFormatter     static var dateFormatter: DateFormatter {         let result = DateFormatter()         result.dateFormat = "yyyy-MM-dd"         return result     } &#9;&#9;func dateText() -> String { &#9;&#9;&#9;&#9;return MyClass.dateFormatter.string(from: Date()) &#9;&#9;} 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
2
0
593
Jan ’21
SwiftUI; Can I use abstraction here?
I'm building a swiftUI app. On smaller iOS devices, I want to show a button that will open a webView. on larger devices, I want to show the webView in the current hierarchy. The following code:     func showNotesView() -> some View {         if horizontalSizeClass == .compact {             return NavigationLink(destination: webView) {                 Text("Episode Notes")             }         }             return webView     } generates a compile error at line1 Function declares an opaque return type, but the return statements in its body do not have matching underlying types 1. Return statement has underlying type 'NavigationLink<Text, some View>' 2. Return statement has underlying type 'some View'
1
1
1.8k
Oct ’20
how to notify swiftUI View when source of truth has changed
I have a somewhat obscure source of truth related to a like/favourite button. When a user taps the button, the model object is added to the favourites list. When they tap again, the model object is removed from the list. The actual button is wired to a custom binding, however it is not getting re-drawn when I add/remove items from the favourites list. What do I need to do to let the button know it needs to redraw? thanks struct Model: Equatable {     var name: String     var isFavourite: Bool {         Favourites.shared.includes(model: self)} } class Favourites {     static let shared = Favourites()     var favList: [Model] = []     func add(model: Model) {         favList.append(model)     }     func remove(model: Model) {         if let index = favList.firstIndex(where: {$0 == model}) {             favList.remove(at: index)         }     }     func includes(model: Model) -> Bool {         return favList.firstIndex(where: {$0 == model}) != nil     } } struct ContentView: View {     var model = Model(name: "Andrew")     var favouriteBinding: Binding<Bool> {         Binding<Bool>(             get: {                 return self.model.isFavourite         },             set: {                 if $0 {                     Favourites.shared.add(model: self.model)                 } else {                     Favourites.shared.remove(model: self.model)                 }         })     }     var body: some View {         FavouriteView(isFavourite: favouriteBinding)     } } struct FavouriteView: View {     @Binding var isFavourite: Bool     var body: some View {         Button(action: {             self.isFavourite.toggle()         }) {             Image(systemName: imageName())                 .foregroundColor(Color.yellow)                 .font(Font.system(size: 40))         }     }     func imageName() -> String {         return isFavourite ? "star.fill" : "star"     } }
2
0
2.6k
Oct ’20
Adding view hierarchy in SwiftUI seems to break combine behaviour...
Apologies for a somewhat complex code snippet, I tried to strip away as many red herrings as possible. Expected Behaviour: tap red circle it animates to green status text updates circle returns to red Actual behaviour: tap red circle it animates to green status text updates circle does NOT return to red I can fix the circle behaviour by removing the @Published modifier from the status property. I can also fix the circle behaviour by copying everything from ExtractedView and implementing it in ContentView. Am I doing something wrong? or is there something I can do differently to make my code work? (I would prefer to not copy everything from extractedView into contentView as my app requires n ExtractedViews and I'd rather not duplicate that code. thanks in advance, Mike class SubModel: ObservableObject {     @Published var isActing: Bool = false     func startActing() {         isActing = true     } } class Model: ObservableObject {     @Published var status: Int = 0 //    var status: Int = 0     let leftModel = SubModel()     func startActingLeft() {         leftModel.startActing()         status += 3     } } struct ContentView: View {     @ObservedObject var model: Model = Model()     var body: some View {         VStack {             ExtractedView(model: model.leftModel)                 .onTapGesture {                     self.model.startActingLeft()             }             Text(String(model.status))         }     } } struct ExtractedView: View {     var model: SubModel     @State var fillColor = Color.red     private var actSubject = PassthroughSubject<Void, Never>()     private var afterAct: AnyPublisher<Void, Never>     init(model: SubModel) {         self.model = model         self.afterAct = actSubject             .debounce(for: .milliseconds(1100), scheduler: DispatchQueue.main)             .eraseToAnyPublisher()     }     var body: some View {         ZStack {             Circle()                 .fill(fillColor)                 .frame(width: 150, height: 150)         }         .onReceive(model.$isActing, perform: {             if $0 {                 withAnimation(Animation.easeInOut(duration: 0.8), {                     self.fillColor = Color.green                 })                 self.actSubject.send()             } else {                 self.fillColor = Color.red             }         })         .onReceive(afterAct) {             self.model.isActing = false         }     } }
0
0
702
Oct ’20
Looking for some help related to Combine...
My app has a PhraseModel object that includes multiple WordModel objects (each word publishes a currentWord value) I now want to add a pipeline that will eventually include debouncing, and possibly even network calls. But for now, I just want to verify that I can declare a basic CombineLatest operator and create a subscriber that fires anytime one of the words changes. (should this exist in the model object? or in the View?) Currently, I don't seem to be able to get my basic baby steps code to compile. (problems in the code below around line 10 and line 24.) I'd be grateful for any high level or low level help on this. I feel like I've read fairly extensively, but nothing I've come across seems to apply perfectly to what I'm doing. (in particular the example at the 26 minute mark of video 721 from wwdc2019 feels tantalizing close. alas it doesn't seem to compile either.) thanks, in advance, Mike class WordModel: ObservableObject {     @Published var currentWord: String     init(word: String) {         self.currentWord = word     } } class PhraseModel: ObservableObject {     let leftWord = WordModel(word: "myLeft")     let rightWord = WordModel(word: "myRight")     var phraseChangedPublisher: AnyPublisher<String, Never> {         return CombineLatest(leftWord.$currentWord, rightWord.$currentWord)             .map{(leftWord, rightWord) -> String                 return leftWord + rightWord             }     } } struct PhraseView: View {     @ObservedObject var model: PhraseModel     var body: some View {         HStack {             Text(model.leftWord.currentWord)             Text(model.rightWord.currentWord)         }         .onReceive(model.phraseChangedPublisher, perform: {             print("hello")         })     } }
1
0
497
Oct ’20
Looking to tweak default behaviour for a SwiftUI NavigationView in a TabView on iPad (in Portrait orientation)
At the top level, my app has a tab bar. the second tab's root view is a list view. When a user taps an item in the list, the app presents a detail view for the list item. The problem I'm currently having is the behaviour the first time the user taps the second tab's button in the tab bar (when running on an iPad in portrait. There is a navBar at the top, but the screen is otherwise empty. (tapping the left button on the navBar shows the list view, which allows the user to select an item which populates the main detail view) Is there some way (in swfitUI) to force the list view to show when in portrait? Alternatively/additionally, is there someway to present some instructional view in the otherwise empty view. (It doesn't appear to be creating a standard detail view here until the user exposes the list and picks an item) Here is some sample code that demonstrates what I'm describing. thanks in advance for any assistance! Mike import SwiftUI struct ContentView: View {     var body: some View {         TabView(){             FirstTabView()                 .tabItem {                     Text("First")                 }                 .tag(0)             SecondTabView()                 .tabItem {                     Text("Second")                 }                 .tag(1)         }     } } struct ContentView_Previews: PreviewProvider {     static var previews: some View {         ContentView()         .previewDevice(PreviewDevice(rawValue: "iPad8,1"))     } } struct FirstTabView: View {     var body: some View {         Text("First View")     } } struct SecondTabView: View {     var body: some View {         NavigationView {             NavigationLink(destination: Text("Detail View")) {                 Text("SummaryView")             }             .navigationBarTitle("Navigation")         }     } }
2
0
825
Sep ’20
swiftUI TabBar selection UserDefaults binding not working as expected
Hello, I have an app with a swiftUI tabBar. It as been working fine for weeks of development. Recently I wanted to persist the selected tab using UserDefaults. The persisting seems to be happening correctly, however my tabBar will now only display the initially selected tab. Trying to switch to another tab just displays a blank view. Is there something wrong in this sample code? struct ContentView: View {     var body: some View {         TabView(selection: Binding&lt;Int&gt;(             get: { Defaults.shared.selectedTabIndex },             set: { Defaults.shared.selectedTabIndex = $0 }         )) {             FirstTab()                 .tabItem {                     Text("First")             }             .tag(0)             SecondTab()                 .tabItem {                     Text("Second")             }             .tag(1)         }     } } struct ContentView_Previews: PreviewProvider {     static var previews: some View {         ContentView()     } } struct FirstTab: View {     var body: some View {         Text("Tab1")     } } struct SecondTab: View {     var body: some View {         Text("Tab2")     } } class Defaults {     static let shared = Defaults()     var selectedTabIndex: Int {         didSet {             UserDefaults.standard.set(selectedTabIndex, forKey: "selectedTabIndex")         }     }     init() {         selectedTabIndex = UserDefaults.standard.integer(forKey: "selectedTabIndex")     } }
2
0
4.2k
Aug ’20
Having trouble changing updating swiftUI List display when sort order changes...
Hello, I am attempting to present a list of items in SwiftUI sorted by when they were last used. Current behaviour: user sees a List of items, sorted with most recently used at the top user selected item X from the list (navigates to its detail view) user 'uses' item X in the detail view user navigates back to list view expected behaviour: item X at the top of the list actual behaviour: item X is still in its previous location (see code below) When I uncomment line 27, tapping the user button immediately (and unexpectedly) takes the user back to the list view. Is there a way I can leave the user on the detail view, but have the list be sorted when I go back to the list view? thanks! import SwiftUI struct ContentView: View {     @ObservedObject var list = ModelList.shared     var body: some View {         NavigationView {             List(list.sorted()) {object in                 NavigationLink(destination: DetailView(object: object)) {                     Text(object.title)                 }             }         }     } } struct ContentView_Previews: PreviewProvider {     static var previews: some View {         ContentView()     } } struct DetailView: View {     var object: ModelObject     var body: some View {         VStack {             Text(object.title)             Button("Use") {                 self.object.usedDate = Date() /*                ModelList.shared.objectWillChange.send() */             }         }     } } class ModelObject: ObservableObject {     @Published var usedDate: Date = Date(timeIntervalSince1970: 0)     let title: String     init(title: String) {         self.title = title     } } extension ModelObject: Identifiable { } class ModelList: ObservableObject {     static let shared = ModelList()     @Published var objects: [ModelObject]     init() {         self.objects = [ModelObject(title: "first"), ModelObject(title: "second")]     }     func sorted() -> [ModelObject] {         return objects.sorted{$0.usedDate > $1.usedDate}     } }
2
0
1.3k
Aug ’20
Trying to update a swiftUI list view when the sort order changes...
Hello, My app includes a list of items sorted by their last used date. I'm trying to implement the following behaviour: user sees a list of items (most recently used at the top) user navigates into detail view for item X in the list user performs an action to 'use' item X user navigates back to the list expected: user sees itemX at the top of the list actual: users sees itemX in its previous location (see sample code below) Note that when I uncomment line 27, after the user uses itemX they are immediately returned to the listView, and itemX is the first item. Is there a way I can get itemX showing at the top, but not getting immediately (jarringly) returned to the listView from the detail view? thanks! import SwiftUI struct ContentView: View {     @ObservedObject var list = ModelList.shared     var body: some View {         NavigationView {             List(list.sorted()) {object in                 NavigationLink(destination: DetailView(object: object)) {                     Text(object.title)                 }             }         }     } } struct ContentView_Previews: PreviewProvider {     static var previews: some View {         ContentView()     } } struct DetailView: View {     var object: ModelObject     var body: some View {         VStack {             Text(object.title)             Button("Use") {                 self.object.usedDate = Date() /*               ModelList.shared.objectWillChange.send() */             }         }     } } class ModelObject: ObservableObject {     @Published var usedDate: Date = Date(timeIntervalSince1970: 0)     let title: String     init(title: String) {         self.title = title     } } extension ModelObject: Identifiable { } class ModelList: ObservableObject {     static let shared = ModelList()     @Published var objects: [ModelObject]     init() {         self.objects = [ModelObject(title: "first"), ModelObject(title: "second")]     }     func sorted() -> [ModelObject] {         return objects.sorted{$0.usedDate > $1.usedDate}     } }
1
0
2k
Aug ’20
How to avoid performing a push navigation in swiftUI
I have a swiftUI listView. Each row includes details about a model object, and a button to perform a specific action in the row. struct EpisodeRowView: View {     @ObservedObject var episode: Episode     var body: some View {         HStack {             if shouldShowRightButton() {                 Button(action: {                     self.handleRightButtonTap()                 }) {                     rightButtonImage()                 }                 .padding(.horizontal)             }             NavigationLink(destination: EpisodeDetailView(episode: episode)) {                 Text(episode.title)             }         }     } Unfortunately when I tap the button it performs the push to the navigation destination. Is there some way I can prevent/disable this unwanted push? thanks, Mike
1
0
1.2k
Aug ’20