Cannot get ScrollView to working within SwiftUI using a VStack - ios

Every time I try to add scrollview, or navigational view to my swiftui code, it causes the app to not load anything from the api. Currently I'm using the OMDB api. Pasted is my movie store and my ContentView. Currently I'm using a custom card view to display the image and the title from the movie store, but after trying to enable scroll view the whole preview of the rendered application goes completely white. And nothing will show up again until I comment out the scroll view. I have tried using a horizontal view and a simple image and text vstack to see if the card view was somehow messing things up with the scroll view.
The tutorial that I have followed for getting this far with some modifications is here: https://www.youtube.com/watch?v=NvyAN81YcO0
Currently I'm running Xcode 11.6.
import Foundation
struct MovieResponse : Decodable{
let movies: [Movie]
private enum CodingKeys:String, CodingKey{
case movies = "Search"
}
}
struct Movie: Decodable{
let imdbID :String
let title: String
let poster: String
let type: String
let year: String
private enum CodingKeys: String, CodingKey{
case imdbID
case title = "Title"
case poster = "Poster"
case type = "Type"
case year = "Year"
}
}
class MovieStore: ObservableObject{
#Published var movies: [Movie]? = [Movie]()
func getAll(){
guard let url = URL(string: "") else{
fatalError("Invalid url")
}
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else{
return
}
let movieResponse = try? JSONDecoder().decode(MovieResponse.self, from:data)
if let movieResponse = movieResponse {
DispatchQueue.main.async{
self.movies = movieResponse.movies
}
}
}.resume()
}
}
import SwiftUI
struct CardView: View {
var image: URLImage
var type: String
var title: String
var year: String
var body: some View {
VStack {
image
.aspectRatio(contentMode: .fit)
HStack {
VStack(alignment: .leading) {
Text(type)
.font(.headline)
.foregroundColor(.secondary)
Text(title)
.font(.title)
.fontWeight(.black)
.foregroundColor(.primary)
.lineLimit(3)
Text(year)
.font(.caption)
.foregroundColor(.secondary)
}
.layoutPriority(100)
Spacer()
}
.padding()
}
.cornerRadius(10)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color(.sRGB, red: 150/255, green: 150/255, blue: 150/255, opacity: 0.1), lineWidth: 1)
)
.padding([.top, .horizontal])
}
}
struct ContentView: View {
#ObservedObject var store: MovieStore
var body: some View {
// ScrollView{
HStack{
ForEach(store.movies ?? [Movie](), id: \.imdbID){ movie in
VStack{
// URLImage(url: movie.poster)
// .frame(width: 100, height: 150)
// Text(movie.title)
// .frame(maxHeight: .infinity, alignment: .top)
CardView(image: URLImage(url:movie.poster), type: movie.type, title: movie.title, year: movie.year)
}
}
}
// }
.onAppear(){
self.store.getAll()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(store: MovieStore())
}
}
Edit:
I was able to generate the app with scrolling with the movies popping up from the api. But only with the list attribute. I noticed that I am getting an error, and am not sure if it affecting the pinging of the api.
2020-08-01 18:28:26.274953-0500 Nyx[19421:1105938] Task <B38BF736-765C-4C69-9ADE-AD889ED7BBE5>.<4> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSUnderlyingError=0x600003c14000 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}, NSErrorFailingURLStringKey=N/A, NSErrorFailingURLKey=N/A, NSLocalizedDescription=unsupported URL}

Related

Error: unrecognized selector sent to instance 0x6000027f1ea0' & Process variables after receive parameters and before render view

I have two views here: RoomDetailView and FeaturedTabView
In the FeaturedTabView, there are two variables imageString and roomImages, the imageString will receive data from RoomDetailView as parameter and RoomImages is for storing an array which is split from imageString after the imageString is assigned with the string coming from RoomDetailView.
I have two questions here:
My app keep crashing and return error: terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Room roomImages]: unrecognized selector sent to instance 0x6000027f1ea0'. However, I am pretty sure that my variable type is correct and is aligned with my coreData attribute type.
May I ask is there anywhere I can first process the string, split to array ( try using init but I dont think that is the correct way to go ) and assign to another roomImages variable before the view get rendered.
struct RoomDetailView: View {
let room: Room;
var body: some View {
ZStack{
VStack(spacing: 0){
FeaturedTabView(imageString: room.roomImages ?? "room-1,")
.padding(.vertical, 20)
})
}
.background(Color.white.ignoresSafeArea(.all, edges: .all))
}
.ignoresSafeArea(.all, edges: .top)
}
}
struct FeaturedTabView: View {
let imageString: String
let roomImages: [String]
var body: some View {
TabView{
ForEach(imageString, id: \.self){
img in FeaturedItemView(img: img)
.padding(.top, 10)
.padding(.horizontal, 15)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
}
init(imageString: String){
self.imageString = imageString
self.roomImages = self.imageString.components(separatedBy: ",")
}
}
Room Entity
Try this example code, using roomImages in the ForEach loop.
// for testing
struct ContentView: View {
var body: some View {
RoomDetailView(room: Room())
}
}
// for testing
struct Room: Identifiable {
let id = UUID()
var roomImages: String? = "room-1, room-2" // <-- for testing
}
struct RoomDetailView: View {
let room: Room
var body: some View {
ZStack{
VStack(spacing: 0) {
FeaturedTabView(imageString: room.roomImages ?? "room-1,")
.padding(.vertical, 20)
}
}
.background(Color.white.ignoresSafeArea(.all, edges: .all))
.ignoresSafeArea(.all, edges: .top)
}
}
struct FeaturedTabView: View {
let imageString: String
let roomImages: [String]
var body: some View {
TabView {
ForEach(roomImages, id: \.self){ img in // <-- here
FeaturedItemView(img: img)
.padding(.top, 10)
.padding(.horizontal, 15)
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
}
init(imageString: String) {
self.imageString = imageString
self.roomImages = self.imageString.components(separatedBy: ",")
}
}
// for testing
struct FeaturedItemView: View {
let img: String
var body: some View {
Text(img)
}
}

SwiftUI code too long for the compiler to type-check it, but I can't seem to break it down into parts

I am trying to make a view iteratively from a JSON file fetched from a PHP URL. The view is made with a for each loop to create an information box for every different station in the JSON and also makes them a navigation link. Ideally, I would tap the box to get more information on a specific tide station (all station data hasn't been presented on the view yet).
Since I am using a for each loop, I represent the current station number in the data array with i. In order to get the same data as the box I click I use the same i number for other relevant data.
My issue is that as I am building everything in the same for each loop to keep that number i in the scope, my code gets too long for the compiler to check. I heard that this could happen even if the code wasn't too long but simply for a typing mistake somewhere, but I have yet to find a mistake that breaks the code and truly believe it is due to the length as it was working if I commented-out some parts.
To resolve that I understand I need to break my code into different sections for the compiler to check them individually so as not to start the process every time.
However, as I am building everything into the for each loop to have the i number in the scope, I cannot make sub-views that use that number.
I am not entirely sure this is the question that would best solve my issue, but how can I pass the for each loop parameter into another view/function/or something else?
I apologize for the code being very rough and not following good coding practices, if not just for SwiftUI, I am quite inexperienced with programming in general. I left the entirety of the code to be sure not to leave any possibility out.
import Foundation
import SwiftUI
import Alamofire
import SwiftyJSON
import MapKit
public var array_tides_site_name = [String]()
public var array_tides_next_lw_time = [String]()
public var array_tides_next_lw_height = [Double]()
public var array_tides_next_hw_time = [String]()
public var array_tides_next_hw_height = [Double]()
public var array_tides_tidal_state = [String]()
public var array_tides_latitude = [Double]()
public var array_tides_longitude = [Double]()
public var array_tides_observed_height = [Double]()
public var array_tides_predicted_height = [Double]()
public var array_tides_surge = [Double]()
struct Previews_GeneralTides_Previews: PreviewProvider {
static var previews: some View {
GeneralTidesView()
}
}
struct GeneralTidesView: View {
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack (alignment: .center) {
Spacer()
Image(systemName: "chart.xyaxis.line")
.font(.largeTitle)
.frame(width: 300, height: 300, alignment: .center)
Spacer()
Text("More Stations")
.font(.title3)
.foregroundStyle(LinearGradient(colors: [.primary, .secondary], startPoint: .topLeading, endPoint: .bottomTrailing))
.frame(width: screenWidth, height: 40)
.background(.thickMaterial)
Spacer()
Divider()
.onAppear() {loadStationData()}
Spacer()
StationList()
}
.navigationBarTitle("Tides")
.navigationBarTitleDisplayMode(.inline)
}
}
}
func loadStationData(){
let generalTideUrl = "http://www.pla.co.uk/hydrographics/ajax/ltoverview.php"
AF.request(generalTideUrl, method: .get).responseJSON(){ (generalTideResponse) in
switch generalTideResponse.result {
case .success:
//print(generaltideresponse.result)
let generalTideResult = try? JSON(data: generalTideResponse.data!)
//print(generaltideresult)
//print(generalTideResult!["tides"])
let generalTideArray = generalTideResult!["tides"]
array_tides_site_name.removeAll()
array_tides_next_lw_time.removeAll()
array_tides_next_lw_height.removeAll()
array_tides_next_hw_time.removeAll()
array_tides_next_hw_height.removeAll()
array_tides_tidal_state.removeAll()
array_tides_latitude.removeAll()
array_tides_longitude.removeAll()
array_tides_observed_height.removeAll()
array_tides_predicted_height.removeAll()
array_tides_surge.removeAll()
for i in generalTideArray.arrayValue {
//print(i)
let site_name = i["site_name"].stringValue
array_tides_site_name.append(site_name)
var next_lw_time = i["next_lw_time"].stringValue
let lwRange = next_lw_time.startIndex..<next_lw_time.index(next_lw_time.startIndex, offsetBy: 11)
next_lw_time.removeSubrange(lwRange)
array_tides_next_lw_time.append(next_lw_time)
let next_lw_height = i["next_lw_height"].doubleValue
array_tides_next_lw_height.append(next_lw_height)
var next_hw_time = i["next_hw_time"].stringValue
let hwRange = next_hw_time.startIndex..<next_hw_time.index(next_hw_time.startIndex, offsetBy: 11)
next_hw_time.removeSubrange(hwRange)
array_tides_next_hw_time.append(next_hw_time)
let next_hw_height = i["next_hw_height"].doubleValue
array_tides_next_hw_height.append(next_hw_height)
let tidal_state = i["tidal_state"].stringValue
array_tides_tidal_state.append(tidal_state)
let latitude = i["latitude"].doubleValue
array_tides_latitude.append(latitude)
let longitude = i["longitude"].doubleValue
array_tides_longitude.append(longitude)
let predictedHeight = i["predicted_height"].doubleValue
array_tides_predicted_height.append(predictedHeight)
let observedHeight = i["observed_height"].doubleValue
array_tides_observed_height.append(observedHeight)
let surge = i["surge_height"].doubleValue
array_tides_surge.append(surge)
}
break
case .failure:
print(generalTideResponse.error!)
break
}
}.resume()
}
struct StationList: View {
#State private var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.5, longitude: -0.12), span: MKCoordinateSpan(latitudeDelta: 0.6, longitudeDelta: 0.6))
var body: some View {
ForEach(0..<array_tides_site_name.count) { i in
NavigationLink(destination: Group{
VStack(alignment: .center) {
stationNavLink()
Text(array_tides_site_name[i])
.font(.largeTitle)
Map(coordinateRegion: $mapRegion, annotationItems: [Location(name: array_tides_site_name[i], coordinate: CLLocationCoordinate2D(latitude: array_tides_latitude[i], longitude: array_tides_longitude[i]))]) { location in
MapMarker(coordinate: location.coordinate)}
.frame(width: screenWidth, height: 250)
HStack {
RoundedRectangle(cornerRadius: 10)
.foregroundStyle(.thinMaterial)
.frame(width: 150, height: 120)
.overlay {
VStack(alignment: .leading , spacing: 10) {
Text("Next Low Tide:")
HStack {Text("Time: "); Text(array_tides_next_lw_time[i])}.foregroundColor(.secondary)
HStack {Text("Height: "); Text(array_tides_next_lw_height[i].description); Text("m")}.foregroundColor(.secondary)
}
}
RoundedRectangle(cornerRadius: 10)
.foregroundStyle(.thinMaterial)
.frame(width: 150, height: 120)
.overlay {
VStack(alignment: .leading, spacing: 10) {
Text("Next High Tide:")
HStack {Text("Time: "); Text(array_tides_next_hw_time[i])}.foregroundColor(.secondary)
HStack {Text("Height: "); Text(array_tides_next_hw_height[i].description); Text("m")}.foregroundColor(.secondary)
}
}
}
Text(array_tides_surge[i].description)
}
}){
ZStack {
RoundedRectangle(cornerRadius: 8)
.strokeBorder(.white.opacity(0.3), lineWidth: 1)
.background(RoundedRectangle(cornerRadius: 8).fill(.thinMaterial))
.frame(width: screenWidth - 40, height: 80)
.overlay() {
VStack(alignment: .leading) {
Spacer()
Text(array_tides_site_name[i])
.padding(.leading, 10)
.foregroundStyle(LinearGradient(colors: [.secondary, .secondary.opacity(0.8)], startPoint: .leading, endPoint: .trailing))
Spacer()
Divider()
Spacer()
Group {
HStack(){
Spacer()
Text("High Water: ")
Text(array_tides_next_hw_time[i])
Spacer()
Text("Low Water: ")
Text(array_tides_next_lw_time[i])
Spacer()
if array_tides_tidal_state[i] == "Flood" { Image(systemName: "arrow.up").foregroundColor(.green) }
else { Image(systemName: "arrow.down").foregroundColor(.red) }
Spacer()
}
}
Spacer()
}
}
}
}
}
}
}
var stationNavLink: some View {
Text(array_tides_surge[currentStation])
}
struct Location: Identifiable {
let id = UUID()
let name: String
let coordinate: CLLocationCoordinate2D
}
Having multiple, associated, arrays to hold your data is a definite code smell. You risk having data get out of sync between the arrays and as you can see, loading and accessing data involves a lot of code.
You can start by creating some [Codable] structs to hold you data. quicktype.io can do this for. Simply paste in your JSON and you get the structs you need.
I have modified Tide to conform to Hashable and Identifiable to make it easier to use in SwiftUI lists.
Also note the use of CodingKeys to convert the properties to Swift naming conventions.
// MARK: - TideData
struct TideData: Codable {
let timezone: String
let tides: [Tide]
}
// MARK: - Tide
struct Tide: Codable, Hashable, Identifiable {
let siteID, siteStation: Int
let siteName: String
let latitude, longitude: Double
let received: String
let observedHeight, predictedHeight, surgeHeight: Double
let nextLwTime: String
let nextLwHeight: Double
let nextHwTime: String
let nextHwHeight: Double
let tidalState: String
var id: Int {
return siteID
}
enum CodingKeys: String, CodingKey {
case siteID = "site_id"
case siteStation = "site_station"
case siteName = "site_name"
case latitude, longitude
case received = "Received"
case observedHeight = "observed_height"
case predictedHeight = "predicted_height"
case surgeHeight = "surge_height"
case nextLwTime = "next_lw_time"
case nextLwHeight = "next_lw_height"
case nextHwTime = "next_hw_time"
case nextHwHeight = "next_hw_height"
case tidalState = "tidal_state"
}
}
Now you can move your loading code into its own class. Make this an ObservableObject and have it publish the tide data.
Since our structs conform to Codable we can use AlamoFire's inbuilt JSON decoding and transformation
lass Loader:ObservableObject {
#Published var tides: [Tide] = []
func load() {
let generalTideUrl = "https://www.pla.co.uk/hydrographics/ajax/ltoverview.php"
AF.request(generalTideUrl, method: .get).responseDecodable(of: TideData.self){ (generalTideResponse) in
switch generalTideResponse.result {
case .success(let tideData):
self.tides = tideData.tides
print(self.tides)
case .failure(let error):
print(error)
}
}
}
}
Much more succinct!
Finally, we can use this data in a SwiftUI view
I have simplified your views for the sake of this answer, but I am sure you will get the idea.
struct GeneralTidesView: View {
#ObservedObject var loader: Loader
var body: some View {
NavigationView {
List {
ForEach(loader.tides) { station in
NavigationLink(destination: StationView(station:station)) {
Text(station.siteName)
}
}
}
}.onAppear {
self.loader.load()
}
}
}
struct Previews_GeneralTides_Previews: PreviewProvider {
static var previews: some View {
GeneralTidesView(loader: Loader())
}
}
struct StationView: View {
var station: Tide
var body: some View {
Form {
HStack {
Text("Predicted Height")
Text("\(station.observedHeight)")
}
HStack {
Text("Predicted Height")
Text("\(station.predictedHeight)")
}
}.navigationTitle(station.siteName)
}
}
You can see how having a struct with properties makes it much easier to pass data around. Also, since all of the properties are in a single struct, we no longer need to work about array indices.

SwiftUI "No arguments" Error when using website links

I'm developing a simple contacts application, when adding the lines shown below, I receive the error: Argument passed to call that takes no arguments.
let websitetext: String
init(){
self.websitetext = contact.website
}
Below is the entire file with irrelevant data removed.
import Foundation
import SwiftUI
struct ContactsDetailView: View{
let contact: Contact
let websitetext: String //lines referenced
init(){
self.websitetext = contact.website
}
var body: some View {
HStack{
VStack {
Image(contact.imageName)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipped()
.cornerRadius(50)
Text(contact.name)
.font(.system(size: 21, weight:.medium, design: .default))
Form{
HStack {
Text ("Phone")
Spacer()
Text(contact.phone)
.foregroundColor(.gray)
.font(.callout)
}
Section{
Link("Website", destination: URL(string: websitetext)!) //Uses the website text variable
}
}
}
}
}
}
struct ResourcesDetailView_Previews: PreviewProvider {
static var previews: some View {
ContactsDetailView(contact: contacts[0]) //ERROR LINE
}
}
Any help to solve this issue would be appreciated!

SwiftUI Change icon by number

I want the Images to change according to the text, but it works over .degisim String and int value in the ready-made code block I found. That's why it doesn't work for String.
import SwiftUI
struct altin: View {
#State var users: [Altin] = []
var body: some View {
ZStack{
List(users) { altin in
NavigationLink {
detayView(altinKur: altin)
} label: {
HStack{
Image(systemName: getImage(percent: Int(altin.degisim)))
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 9, height: 9)
.foregroundColor(
(altin.degisim).contains("-") ?
Color.down :
Color.up)
.padding(.trailing, 5)
Text(altin.degisim)
.font(.system(size: 16))
.fontWeight(.medium)
.foregroundColor(
(altin.degisim).contains("-") ?
Color.down :
Color.up)
}
}
}
}
.listStyle(PlainListStyle())
.padding(0)
.onAppear {
apiCall().getUsers { (users) in
self.users = users
}
}
}
}
struct altin_Previews: PreviewProvider {
static var previews: some View {
altin()
}
}
func getImage(percent: Int) -> String{
if percent > 0 {
return "chevron.up"
}else{
return "chevron.down"
}
}
.foregroundcolor works but not within Image.
The code is more complex, I simplified it for solution.
Model:
struct Altin: Codable, Identifiable {
let id = UUID()
let name: String
let alis: String
let satis: String
let degisim: String
}

SwiftUI NavigationLink not sending data to another view [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
This is my contentView.swift file. I am trying to send data from one view to another view using navigationLink but I can't. I am learning the swiftUI and iOS development. So I am confused what I did wrong.
import SwiftUI
import URLImage
struct Response: Codable {
var results: [Result]
}
struct Result: Codable {
var trackId: Int
var trackName: String
var collectionName: String
var artworkUrl100: URL
}
struct ContentView: View {
#State var results = [Result]()
var body: some View {
NavigationView {
List(results, id: \.trackId) { item in
NavigationLink(
destination: SingleView(item: item),
label: {
HStack() {
URLImage(item.artworkUrl100) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: /*#START_MENU_TOKEN#*/100/*#END_MENU_TOKEN#*/, height: /*#START_MENU_TOKEN#*/100/*#END_MENU_TOKEN#*/, alignment: /*#START_MENU_TOKEN#*/.center/*#END_MENU_TOKEN#*/)
.cornerRadius(5.0)
}
VStack(alignment: .leading) {
Text(item.trackName)
.font(.headline)
Text(item.collectionName)
.font(.subheadline)
.foregroundColor(.secondary)
Spacer()
}
}
})
}
.onAppear(perform: loadData)
.navigationTitle("Native App")
}
}
func loadData() {
guard let url = URL(string: "https://itunes.apple.com/search?term=arijit&entity=song") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
// we have good data – go back to the main thread
DispatchQueue.main.async {
// update our UI
self.results = decodedResponse.results
}
// everything is good, so we can exit
return
}
}
// if we're still here it means there was a problem
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I want to send value to SingleView.swift page . but no luck.
My SingleView.swift code is:
import SwiftUI
struct SingleView: View {
let item: Result
var body: some View {
Text("")
}
}
struct SingleView_Previews: PreviewProvider {
static var previews: some View {
SingleView(item: Result)
}
}
Please let me know where I did wrong. Thank you.
Working fine with some changes to SingleView
import SwiftUI
struct SingleView: View {
let item: Result
var body: some View {
Text(item.trackName)
}
}
struct SingleView_Previews: PreviewProvider {
static var previews: some View {
SingleView(item: Result(trackId: 1, trackName: "Track Name", collectionName: "Coll name", artworkUrl100: URL(string: "https://stackoverflow.com/questions/68556148/swiftui-navigationlink-not-sending-data-to-another-view")!))
}
}

Resources