I am fairly new to SwiftUI and I am attempting to follow a WWDC-2019 app presentation called "Introducing SwiftUI: Building Your First App". so far, everything is working fine except that images are not rendering on the view as expected. My assets xcasettes are properly loaded with nine thumbnails images and each image is affixed with a room name. Below is my the code for the contentView:
import SwiftUI
struct ContentView: View {
var rooms: [Room] = []
var body: some View {
List(rooms) { room in
Image(room.thumbnailName)
VStack (alignment: .leading){
Text(room.name)
Text("\(room.capacity) people")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(rooms: testData)
}
}
The code that follows below is an identifiable struct called "Room" and data called "testData"
import SwiftUI
struct Room : Identifiable {
var id = UUID()
var name: String
var capacity: Int
var hasVideo: Bool = false
var imageName: String { return name}
var thumbnailName: String { return name + "Thumb"}
}
let testData = [
Room(name: "Observation Deck", capacity: 6, hasVideo: true),
Room(name: "Executive Suite", capacity: 8, hasVideo: false),
Room(name: "Charter Jet", capacity: 16, hasVideo: true),
Room(name: "Dungeon", capacity: 10, hasVideo: true),
Room(name: "Panorama", capacity: 12, hasVideo: false),
Room(name: "Oceanfront", capacity: 8, hasVideo:false),
Room(name: "Rainbow Room", capacity: 10, hasVideo: true),
Room(name: "Pastoral", capacity: 7, hasVideo: false),
Room(name: "Elephant Room", capacity: 1, hasVideo: false)
]
I tried changing the name of the thumbnails to "thumbnailName", "Thumb", "room" but neither worked. I haven't been able to associate the word "Thumb" to anything in the code and I suspect it may be the root of the problem.
As requested, below is a screenshot of the contents of the assets:
Contents of my assets
Change the line:
var thumbnailName: String { return name + "Thumb"}
To this:
var thumbnailName: String { return name}
The images don‘t change automatically their size just by adding „thumb“ somewhere. Either you add small images with the „thumb“-name in the assets or you just change the size of your images by adding resizable and frame.
The images don‘t change automatically their size just by adding "thumb" in the file ROOM where have your struct code then quit the word "thumb" after go and put the next line of code down of Image (room.thumbnailName)
ROMS file:
var thumbnailName: String {return name}
contentView:
Image (room.thumbnailName)
.resizable()
.frame(width: 32.0, height: 32.0)
Related
I'm following Apple's app dev Scrumdinger tutorial and I ran into an issue in the "Creating a navigation hierarchy" section. When I use the ForEach loop to iterate through the DailyScrum array and make a navigation link for each piece of data, the link works in the preview but not in the simulator.
This is the code with the ForEach loop:
import SwiftUI
struct ScrumsView: View {
let scrums: [DailyScrum]
var body: some View {
List {
ForEach(scrums) { scrum in
NavigationLink(destination: DetailView(scrum: scrum)) {
CardView(scrum: scrum)
}
.listRowBackground(scrum.theme.mainColor)
}
}
.navigationTitle("Daily Scrums")
.toolbar {
//this button will have an action later
Button(action: {}) {
Image(systemName: "plus")
}
.accessibilityLabel("New Scrum")
}
}
}
struct ScrumsView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ScrumsView(scrums: DailyScrum.sampleData)
}
}
}
Here is the code where I defined the DailyScrum array:
import Foundation
struct DailyScrum: Identifiable {
let id: UUID
var title: String
var attendees: [Attendee]
var lengthInMinutes: Int
var theme: Theme
init(id: UUID = UUID(), title: String, attendees: [String], lengthInMinutes: Int, theme: Theme) {
self.id = id
self.title = title
self.attendees = attendees.map{ Attendee(name: $0)}
self.lengthInMinutes = lengthInMinutes
self.theme = theme
}
}
extension DailyScrum {
struct Attendee: Identifiable {
let id: UUID
var name: String
init(id: UUID = UUID(), name: String) {
self.id = id
self.name = name
}
}
}
extension DailyScrum {
static let sampleData: [DailyScrum] =
[
DailyScrum(title: "Design", attendees: ["Cathy", "Daisy", "Simon", "Jonathan"], lengthInMinutes: 10, theme: .yellow),
DailyScrum(title: "App Dev", attendees: ["Katie", "Gray", "Euna", "Luis", "Darla"], lengthInMinutes: 5, theme: .orange),
DailyScrum(title: "Web Dev", attendees: ["Chella", "Chris", "Christina", "Eden", "Karla", "Lindsey", "Aga", "Chad", "Jenn", "Sarah"], lengthInMinutes: 5, theme: .poppy)
]
}
The NavLink works as expected in the preview screen but it doesn't work when I run the simulator. Each scrum is greyed out and I can't click on anything.
Do not forget to embed the ScrumsView into a NavigationView
I'm trying to achieve a design which essentially has the following layout:
So I need column headings for this table or grid view. The second column does not need a heading.
This seems like a job for a LazyVGrid or LazyHGrid, however I can't seem to find a solution which neatly includes headings like this - it all seems a bit hacky.
Wondering if I am missing a better way to achieve this. I could of course try and create VStacks within HStacks, but this just seems like something which should be accomplished more intelligently than this.
You can just pass the headers into the LazyVGrid before you show the content:
struct Item: Identifiable {
let id = UUID()
var item: String
var description: String = "This is the item description"
var quantity: Int = 1
var price: Double = 0
}
struct ContentView: View {
let data = [
Item(item: "Image 1", quantity: 2, price: 1.99),
Item(item: "Image 2", quantity: 1, price: 3.99),
Item(item: "Image 3", quantity: 5, price: 9.99),
]
let columns = [
GridItem(.flexible(), alignment: .topLeading),
GridItem(.flexible(minimum: 150), alignment: .topLeading),
GridItem(.flexible(), alignment: .topLeading),
GridItem(.flexible(), alignment: .topLeading),
]
var body: some View {
LazyVGrid(columns: columns) {
// headers
Group {
Text("Item")
Text("")
Text("Qty")
Text("Price")
}
.font(.headline)
// content
ForEach(data) { item in
Text(item.item)
Text(item.description)
Text("\(item.quantity)")
Text("$\(item.price, specifier: "%.2f")")
}
}
.padding()
}
}
I am trying to display images stored in an array which I am providing as a collection to ForEach and giving it a HStack view to display the result. But for life of me, can't fathom why HStack is returning a "VStack" sort of view?
Code:
import SwiftUI
struct ContentView: View {
var body: some View {
Home()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Home: View {
// 40 = padding horizontal
// 60 = 2 card to right side...
var width = UIScreen.main.bounds.width - (40 + 60)
var height = UIScreen.main.bounds.height/2
var books = [
Book(id: 0, image: "p1", offset: 0),
Book(id: 1, image: "p0", offset: 0),
Book(id: 2, image: "p3", offset: 0),
Book(id: 3, image: "p2", offset: 0),
Book(id: 4, image: "p5", offset: 0),
Book(id: 5, image: "p4", offset: 0),
]
var body: some View{
ForEach(books.reversed()) { book in
HStack{
Image(book.image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: width, height:getheight(index: book.id))
.cornerRadius(25)
.shadow(color: Color.black.opacity(0.5), radius: 5, x: 5)
}
}
}
func getheight(index: Int)->CGFloat{
return height - (index < 3 ? CGFloat(index) * 40 : 80)
}
}
struct Book : Identifiable {
var id: Int
var image : String
var offset : CGFloat
}
I have stripped the code to barebones to highlight the issue and have attached my output screenshot for clarity. Please help.
.
The problem is the HStack being inside the ForEach. You are not aligning every view in an HStack, but just each separate view in its own HStack. It seems that by default SwiftUI prefers a vertical layout.
Consider this incorrect code:
struct ContentView: View {
var body: some View {
ForEach(1 ..< 10) { row in
HStack {
Text(String(row))
}
}
}
}
And this correct code:
struct ContentView: View {
var body: some View {
HStack {
ForEach(1 ..< 10) { row in
Text(String(row))
}
}
}
}
These are the results. Left is HStack inside the ForEach, and right is HStack outside the ForEach:
I've never done a post on this before, so hopefully this is set up correctly.
I'm new to swift, and I want to create a button that adds a new item to a list that contains a navigation link to a file that it would create with linked data from the previous item, and I haven't found any way to do this after spending a few days on research and testing.
This is what my app currently looks like for the layout I want in the end: Q1 , and here is a preview of the different Q1-4 Views I mentioned: Q1-4
I know it's a lot, so let me explain more in depth: I want to have a list contained in what is called 'Q1' (as seen above) that starts out with 'Week 1', and as you click the add button, I want it to add a 'Week 2' and so forth up to 10 weeks. Once you hit 10 weeks, I want the user to have to change to the different view, 'Q2', which then they can add Week 11-20, and so forth until Q4, which limits it to a total of 40 Weeks. I want each week to contain a navigation link to a new view; however, I also want data from the previous week to be carried over as soon as I create the new week, so the user won't have to manually put in the previous week's data.
I know how to do some of this by using a JSON file for the numbers, as I've seen tutorials on that, however, I don't see a point to this, as the only data I need for the Week numbers are 1-40, but I can't seem to get it to work with an array or anything. I do know that I can use an #EnvironmentObject to get the data I need from the other pages, but I'm not exactly sure how to set that up either. Other than that, I'm stuck! Here is my code:
import SwiftUI
struct BillsView: View {
#State private var quarterNumber = 0
let quarterNumbers = [1, 2, 3, 4]
var body: some View {
NavigationView{
VStack {
Section {
Picker("Quarter Number", selection: $quarterNumber) {
ForEach(0 ..< quarterNumbers.count) {
Text("Q\(self.quarterNumbers[$0])")
}
}
.pickerStyle(SegmentedPickerStyle())
.padding(.horizontal)
if quarterNumber == 0 {
Q1View()
} else if quarterNumber == 1 {
Q2View()
} else if quarterNumber == 2 {
Q3View()
} else if quarterNumber == 3 {
Q4View()
}
}
Spacer()
}
.navigationBarTitle("Bills")
.navigationBarItems(leading: EditButton(),
trailing: Button(action: {
//Adds the new week
}){
Image(systemName: "plus.circle.fill")
})
}
}
}
struct Q1View: View {
#State private var weekNumber = 0
let weekNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var body: some View {
List {
NavigationLink(destination: Week1View()) {
Text("Week 1")
}
}
}
}
struct Week1View: View {
var body: some View {
List {
link(label: "Gross Income", destination: GrossIncome())
link(label: "Expenses", destination: Expenses())
}.navigationBarTitle(Text("Week 1"), displayMode: .inline)
}
private func link<Destination: View>(label: String, destination: Destination) -> some View {
return NavigationLink(destination: destination) {
Text(label)
}
}
}
I am not quite sure whether i understood you right, but i made a very simple example which answers the question of your title
import SwiftUI
struct ContentView: View {
#State private var list : [String] = ["Chris"]
#State private var quarterNumber = 0
var body: some View {
Group () {
Button(action: {
self.list.append("whatever")
}) {
Text("tap me")
}
NavigationView{
List(list, id: \.self) { item in
NavigationLink(
destination: View2(text: "hallo")
.navigationBarTitle(Text("Categories"), displayMode: .automatic)
) {
Text("Categories")
}.isDetailLink(false) // damit der Doof nicht rechts das nächste Menu öffnet
}
}
}
}
}
struct View2: View {
var text : String
var body: some View {
Text(text)
}
}
I'm trying to animate the hiding/showing of a picker based on a toggle. If true or false, I would like the picker to easeInOut.
I've tried adding the .animation(Animation.easeInOut(duration: 0.5)) to the picker itself or the HStack the picker is in, but both add the animation to values inside the picker and when scrolling through the values the application to crashes.
HStack {
if showPicker {
Picker(selection: $selected.value, label: Text(selected.type)) {
ForEach(digits, id: \.self) { d in
Text("\(d)")
}
}
.frame(width: 40)
}
}
.animation(Animation.easeInOut(duration: 2))
if showPicker {
Picker(selection: $selected.value, label: Text(selected.type)) {
ForEach(digits, id: \.self) { d in
Text("\(d)")
}
}
.frame(width: 40)
.animation(Animation.easeInOut(duration: 0.5))
}
Both options do animate the hiding/showing the picker, but it also animates scrolling through the values in the picker, which causes it to crash.
Any help would be appreciated.
Thank you
About your first approach, putting animation on HStack. Never do that. According to the comments in the declaration file:
Use this modifier on leaf views rather than container views. The
animation applies to all child views within this view; calling
animation(_:) on a container view can lead to unbounded scope.
I tried your second approach (filling the missing bits from your post), and it won't crash. Maybe you can update your question with a fully reproducible example.
Changed animation to explicit, so other parameters are not affected:
struct PickerStackOverflow: View {
#State private var showPicker = true
#State private var value: Int = 1
let digits: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
var body: some View {
VStack {
Text("Selected \(value)")
HStack {
if showPicker {
Picker(selection: $value, label: Text("Label")) {
ForEach(digits, id: \.self) { d in
Text("\(d)")
}
}
.frame(width: 40)
}
}
Button("Tap Me") {
withAnimation(Animation.easeInOut(duration: 2)) {
self.showPicker.toggle()
}
}
}
}
}