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!")
}
}
}
Post
Replies
Boosts
Views
Activity
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?
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()
}
}
})
}
}
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()
}
}
}
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
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'
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"
}
}
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
}
}
}
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")
})
}
}
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")
}
}
}
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<Int>(
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")
}
}
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}
}
}
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}
}
}
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