.isFavorite results in errors - error-handling

I'm trying to place an inFavorite on a Detailed View but get the following errors:
-Cannot convert value of type 'Binding' to expected condition type 'Bool'
-Value of type 'ObservedObject.Wrapper' has no dynamic member 'isFavorite' using key path from root type 'TitleModel'
import SwiftUI
struct DetailView: View {
#EnvironmentObject var model: TitleModel
var detail: Title
var body: some View {
ScrollView {
VStack (alignment: .leading) {
//MARK: Detail Image
Image(detail.image1)
.resizable()
.scaledToFill()
//MARK: Divider
Divider()
if $model.isFavorite {
Image(systemName: "star.fill")
.imageScale(.medium)
.foregroundColor(.yellow)
}
Divider()
//MARK: Remark
VStack(alignment: .leading) {
Text("Remark:")
.font(.headline)
.padding([.bottom, .top], 5)
ForEach(detail.remark, id:\.self) {item in
Text("• " + item)
}
}
.padding(.horizontal)
//MARK: Reference(s)
VStack(alignment: .leading) {
Text("Reference(s):")
.font(.headline)
.padding([.bottom, .top], 5)
}
.padding(.horizontal)
}
}
.navigationBarTitle(detail.title)
}
}
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
//create a dummy title and pass it into the detail view so we can see a preview
let model = TitleModel()
DetailView(detail: model.titles[0])
}
}
The view works perfect until I throw in the .isFavorite code. 'var model' references my View Model for my JSON data 'titles'. I am attempting to allow the person to tap the .star to highlight the view. I will then have a Scroll View with only the favorites. Thanks for helping me out.

Related

Back button skipping a view in View1 -> View2 -> View3 NavigationLink

I'm fairly new to Swift and it may be I'm going about this all wrong, but I have a bug I can't work out - within a NavigationView, a NavigationLink in View1 opens to View 2, then that view has a NavigationLink to View3. View3 shows the title from View1, which is obviously wrong, and when the back button is pressed it also goes back to View1 rather than View2. Am I using these links wrong or is it a SwiftUI issue? Simplified example of what I'm doing below.
struct FirstView: View {
var body: some View {
Text("Hello, World #1!")
.navigationBarItems(trailing:
NavigationLink(destination: SecondView()){
Image(systemName: "folder.badge.plus")
}
)
}
}
struct SecondView: View {
var body: some View {
Text("Hello, World #2!")
.navigationBarItems(trailing:
NavigationLink(destination: ThirdView()){
Image(systemName: "folder.badge.plus")
}
)
}
}
struct ThirdView: View {
var body: some View {
Text("Hello, World #3!")
}
}
Funny enough I had the same problem with the navigation skipping one view. Your problem is different though. The first view only will have the NavigationView declared. After that you just use navigation links.
Refactoring:
import SwiftUI
struct FirstView: View {
var body: some View {
NavigationView {
VStack {
Text("Hello, World #1!")
.padding()
NavigationLink(destination: SecondView()){
Image(systemName: "folder.badge.plus")
}
}
// a navigationBarTitle modifier inside!
.navigationBarTitle(Text("Hello"))
}
}
}
struct SecondView: View {
var body: some View {
VStack {
Text("Hello, World #2!")
.padding()
NavigationLink(destination: ThirdView()){
Image(systemName: "folder.badge.plus")
}
}
}
}
struct ThirdView: View {
var body: some View {
Text("Hello, World #3!")
}
}
struct FirstView_Previews: PreviewProvider {
static var previews: some View {
FirstView()
}
}

Can I disable Navigationlink for a specified set of cells in my SwiftUI list?

I am working on a contacts app in SwiftUI for work which will display hundreds of phone numbers. Some contacts have a detail view with address and alternate numbers, while some contacts do not.
For example, Harry Smith will have basic phone extension listed in row while his cell number and address are listed in detail view.
But the number for the Second Floor Bathroom only needs the basic phone extension in the row, a detail view containing cell number and address for the bathroom is unnecessary.
How do I disable navigation link for those contacts who do not have pertinent detail info?
I prefer to add NavigationLink on condition.
I will have a "Cell" with some contents. (image/file detail)
A - build a "content" View:
struct MediumFileContentRow: View {
var file: MediumFile
var body: some View {
VStack (alignment: .leading) {
let (name, attribs) = file.getInfo() // get from model..
Text(name)
Spacer()
Text(attribs)
}// VStack
}
}
B - Your cell (MediumFileRow) will conditionally add link (NavigationLink) "around" our content view, and will give back different Views using AnyView:
struct MediumFileRow: View {
var file: MediumFile
var body: some View {
let enabled = NavigationLink(destination: Detail(t: file.name!)) {
MediumFileContentRow(file: file)
}
let disabled = MediumFileContentRow(file: file)
return file.isEmpty ? AnyView(disabled) : AnyView(enabled)
}
All (almoast..) The code:
struct MediumFileContentRow: View {
var file: MediumFile
var body: some View {
VStack (alignment: .leading) {
let (s,sd) = file.displayed()
Text(s)
Spacer()
Text(sd).font(.system(size: 9.0))
}// VStack
}
}
struct MediumFileRow: View {
var file: MediumFile
var body: some View {
let enabled = NavigationLink(destination: Detail(t: file.name!)) {
MediumFileContentRow(file: file)
}
let disabled = MediumFileContentRow(file: file)
return file.isEmpty ? AnyView(disabled) : AnyView(enabled)
}
...
..
In list You will have:
var body: some View {
NavigationView{
....
List {
ForEach(mediumFiles, id: \.self) { file in
MediumFileRow(file: file)
}
}
......
advantage: You can easily customise "disabled" cells.

ScrollView SwiftUI

I'am developing an app with swift ui. It's a single View App. But I have a problem with the scrollView
I have tried to delete the scrollview but it didn't change anything. I always have this big white problem on the top of my view.
https://imgur.com/a/BiWy0fe
import SwiftUI
struct Redactor {
var id: Int
let name, imageURL, since, bio: String
}
struct Article {
var id: Int
let name, image, content, redactor: String
}
struct MainScreenView: View {
let redactors:[Redactor] = [...]
let articles:[Article] = [...]
var body: some View {
NavigationView {
ScrollView(.vertical, showsIndicators: true) {
VStack(alignment: .leading) {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 20) {
ForEach(redactors, id: \.id) { Redactor in
NavigationLink(destination: MainRedactorView(redactor: Redactor)) {
RedactorView(redactor: Redactor)
}
}
}.padding(.leading, 15)
}.padding(.top, 10)
VStack(alignment: .leading) {
Text("Les Dernières nouvelles :")
.font(.title)
.fontWeight(.bold)
.padding(.leading, 15)
.padding(.top, 20)
ForEach(articles, id: \.id) { Article in
ArticleView(article: Article)
}
}
}
}
.navigationBarTitle(Text("The News Place"))
}
}
}
struct RedactorView: View {
let redactor : Redactor
var body : some View {
VStack {
Image(redactor.imageURL)
.resizable()
.frame(width: 150, height: 150)
.aspectRatio(contentMode: .fill)
.cornerRadius(75)
Text(redactor.name)
.font(.subheadline)
.fontWeight(.bold)
}
}
}
struct ArticleView: View {
var article: Article
var body: some View {
VStack (alignment: .leading) {
Image(article.image)
.renderingMode(.original)
.resizable()
.cornerRadius(10)
.aspectRatio(contentMode: .fit)
Text(article.name)
.font(.title)
.fontWeight(.bold)
Text("Le Rondeau Mag'")
.font(.subheadline)
.foregroundColor(.red)
Text(article.content)
.font(.body)
.lineLimit(5)
.foregroundColor(.secondary)
}.padding()
}
}
I would like to delete this white blank on the top of my scroll view. Thank you
Did you try deleting the VStack inside ScrollView? ScrollView already defines an implicit stack.
A good way to know which element causes the blank space is applying .border(Color.red) to different views.
Hope this helps.
Is the NavigationView that's creating the white space on top.
Try with
.navigationBarTitle(Text("The News Place"), displayMode: .inline)
and see if the white space disappears.

SwiftUI with ObservableObject - List having NavigationLink to details downloading it on scroll NOT on appear

I have list of cities
struct CityListView: View {
#ObservedObject private(set) var citiesViewModel: CitiesViewModel
var body: some View {
LoadingView(isShowing: .constant(citiesViewModel.cities?.isEmpty ?? false)) {
NavigationView {
List(self.citiesViewModel.cities ?? []) { city in
NavigationLink(destination: DetailView(cityName: city.name,
detailCityModel: DetailCityModel(cityId: city.id))) {
Text(city.name)
}
}
.navigationBarTitle(Text("Cities"), displayMode: .large)
}
}
}
}
and when I'm scrolling the list, the DetailCityModel inits and download data from API. How to downloading (or init DetailCityModel) on DetailView's appearance, not for showing item with NAvigationLink to DetailView?
You have to kick off the API call in onAppear() not in the initialiser of DetailView.

Load more functionality using SwiftUI

i have used ScrollView with HStack, now i need to load more data when user reached scrolling at last.
var items: [Landmark]
i have used array of items which i am appeding in HStack using ForEach
ScrollView(showsHorizontalIndicator: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(self.items) { landmark in
CategoryItem(landmark: landmark)
}
}
}
What is the best possible solution to manage load more in SwiftUI without using custom action like loadmore button.
It's better to use ForEach and List for this purpose
struct ContentView : View {
#State var textfieldText: String = "String "
private let chunkSize = 10
#State var range: Range<Int> = 0..<1
var body: some View {
List {
ForEach(range) { number in
Text("\(self.textfieldText) \(number)")
}
Button(action: loadMore) {
Text("Load more")
}
}
}
func loadMore() {
print("Load more...")
self.range = 0..<self.range.upperBound + self.chunkSize
}
}
In this example each time you press load more it increases range of State property. The same you can do for BindableObject.
If you want to do it automatically probably you should read about PullDownButton(I'm not sure if it works for PullUp)
UPD:
As an option you can download new items by using onAppear modifier on the last cell(it is a static button in this example)
var body: some View {
List {
ForEach(range) { number in
Text("\(self.textfieldText) \(number)")
}
Button(action: loadMore) {
Text("")
}
.onAppear {
DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 10)) {
self.loadMore()
}
}
}
}
Keep in mind, that dispatch is necessary, because without it you will have an error saying "Updating table view while table view is updating). Possible you may using another async way to update the data
If you want to keep using List with Data instead of Range, you could implement the next script:
struct ContentView: View {
#State var items: [Landmark]
var body: some View {
List {
ForEach(self.items) { landmark in
CategoryItem(landmark: landmark)
.onAppear {
checkForMore(landmark)
}
}
}
}
func checkForMore(_ item: LandMark) {
guard let item = item else { return }
let thresholdIndex = items.index(items.endIndex, offsetBy: -5)
if items.firstIndex(where: { $0.id == item.id }) == thresholdIndex {
// function to request more data
getMoreLandMarks()
}
}
}
Probably you should work in a ViewModel and separate the logic from the UI.
Credits to Donny Wals: Complete example