Why are Image objects not showing up from a SwiftUI tutorial? - ios

I've been trying to change the layout to the WatchLandmarks tutorial that Apple provides for making SwiftUI apps for the watch (https://developer.apple.com/tutorials/swiftui/creating-a-watchos-app) and I can't figure out why the images aren't loading for me. It works just fine in the completed tutorial code, and the only differences are in my changes to the layout of LandmarkList and LandmarkRow.
changes to LandmarkList:
struct LandmarkList<DetailView: View>: View {
#EnvironmentObject private var userData: UserData
let detailViewProducer: (Landmark) -> DetailView
var body: some View {
VStack(alignment: .leading){
Text(" Landmarks")
.font(.system(size: 16))
ScrollView(.horizontal, showsIndicators:false){
HStack(alignment: .center){
ForEach(userData.landmarks) { landmark in
// if !self.userData.showFavoritesOnly || landmark.isFavorite {
NavigationLink(
destination: self.detailViewProducer(landmark).environmentObject(self.userData)) {
LandmarkRow(landmark: landmark)
.frame( maxWidth: 70, maxHeight: 70, alignment: .leading)
}
// }
}
}
}.frame(minWidth: 0, maxWidth: .infinity, maxHeight: 100, alignment: .center)//.frame(width:150, height: 100)
Text(self.userData.landmarks[0].name)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 24, alignment: .center)
HStack(spacing: 32){
Button(action: { print("first button") }) {
Text("P")
}.frame(minWidth: 20, maxWidth:50, minHeight: 20, maxHeight:50)
.background(Color.orange)
.clipShape(Circle())
.overlay(Circle().stroke(Color.clear, lineWidth: 4))
.shadow(radius: 10)
Button(action: { print("second button") }) {
Text("S")
}.frame(minWidth: 20, maxWidth:50, minHeight: 20, maxHeight:50)
.background(Color.orange)
.clipShape(Circle())
.overlay(Circle().stroke(Color.clear, lineWidth: 4))
.shadow(radius: 10)
}.frame(minWidth: 0, maxWidth: .infinity, maxHeight: 100, alignment: .center)
// List {
// Toggle(isOn: $userData.showFavoritesOnly) {
// Text("Show Favorites Only")
// }
//
// ForEach(userData.landmarks) { landmark in
// if !self.userData.showFavoritesOnly || landmark.isFavorite {
// NavigationLink(
// destination: self.detailViewProducer(landmark).environmentObject(self.userData)) {
// LandmarkRow(landmark: landmark)
// }
// }
// }
// }
// .navigationBarTitle(Text("Landmarks"))
}.frame(maxWidth: .infinity, minHeight:200, maxHeight: .infinity, alignment: .leading)
}
}
changes to LandmarkRow
struct LandmarkRow: View {
var landmark: Landmark
var body: some View {
HStack {
landmark.image
.resizable()
.frame(width: 50, height: 50)
.cornerRadius(8.0)
// Text(landmark.name)
// Spacer()
//
// if landmark.isFavorite {
// Image(systemName: "star.fill")
// .imageScale(.medium)
// .foregroundColor(.yellow)
// }
}
}
}
What am I doing wrong?

Try to call .renderingMode(.original) on the image.
There are two types of rendering modes for your image assets:
Original
Template
Original mode will portray the image asset as it is, while the template mode will turn all the non-transparent parts of the image into one color that you can set. The default color is black.
Note: No overview available is available in Apple documentation.
In this tutorial, you’ll learn what rendering mode in SwiftUI is and how to use it.

Related

swiftUI form bug on XCode13 and iOS15 and iPadOS15

I was building a form on Xcode 12 and using iOS 14 and everything was working as expected. But after switching to 13 and iOS 15, the height of the cells are inconsistent, even changing when scrolled offscreen like they were a reusable table cell.
For example, I have this code: *** EDIT: I changed the below code to have everything that's currently on that page.
Struct EvalViewMHx: View {
var body: some View {
VStack {
Form {
Label(
title: {
Text("Demographics & Medical History")
.font(.title)
.foregroundColor(.white)
.padding()
},
icon: {
Image(systemName: "heart.text.square.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.white)
})
.listRowBackground(Color.blue)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
Section(header: Text("Demographics")) {
demographicsView(nameText: $nameText, DOB: $DOB, height: $height, weight: $weight)
}
Section(header: Text("Medical History")) {
VStack {
HStack {
Text("When did you get hurt?")
.padding()
Spacer()
Toggle("", isOn: $doesNotKnowInjuryDate)
.frame(width: 35)
.padding(.horizontal)
Text("I don't know")
.fontWeight(!doesNotKnowInjuryDate ? .regular : .bold)
.padding(.leading)
}
if !doesNotKnowInjuryDate {
DatePicker("Injury Date", selection: $injuryDate, displayedComponents: [.date])
.foregroundColor(Color(UIColor.systemGray3))
.padding()
}
}
.frame(minWidth: .zero, idealWidth: .infinity, maxWidth: .infinity, minHeight: 150, idealHeight: 150, maxHeight: 150, alignment: .center)
VStack(alignment: .leading) {
HStack {
Text("Where is your injury?")
Spacer()
Text("Right")
.fontWeight(isInjuryOnRightSide ? .regular : .bold)
Toggle("", isOn: $isInjuryOnRightSide)
.toggleStyle(SwitchToggleStyle(tint: Color(UIColor.systemGray5)))
.frame(width: 35)
.padding(.horizontal)
Text("Left")
.fontWeight(!isInjuryOnRightSide ? .regular : .bold)
.padding(.leading)
}
.zIndex(2.0)
Picker(selection: $injuryLocation, label: Text(""), content: {
ForEach(injuryLocations, id: \.self) { location in
Text(location)
}
})
.pickerStyle(WheelPickerStyle())
.frame(minWidth: .zero, idealWidth: .infinity, maxWidth: .infinity, minHeight: 100, idealHeight: 100, maxHeight: 100, alignment: .center)
.zIndex(1.0)
.padding()
}
.padding()
VStack(alignment: .leading) {
Text("How did you get hurt? (Method of Injury)")
.padding()
TextEditor(text: $MOI)
.frame(height: 150)
.background(Color(UIColor.systemGray5).opacity(0.75))
.cornerRadius(15)
}
.frame(minWidth: .zero, idealWidth: .infinity, maxWidth: .infinity, minHeight: 200, idealHeight: 200, maxHeight: 200, alignment: .center)
.padding()
if injuryLocation != "Lower Back" && injuryLocation != "Upper Back" && injuryLocation != "Head / Neck" && injuryLocation != "Chest" {
VStack {
pastInjuryHistory(injuryLocation: $injuryLocation, hasHadSameSideInjury: $hasHadSameSideInjury, hasHadOppositeSideInjury: $hasHadOppositeSideInjury, isSameSide: true)
pastInjuryHistory(injuryLocation: $injuryLocation, hasHadSameSideInjury: $hasHadSameSideInjury, hasHadOppositeSideInjury: $hasHadOppositeSideInjury, isSameSide: false)
}
}
}
}
}
}
}
(In the picture below, you're also seeing some buttons at the top, which are just rectangles in a horizontal scroll view. The code above is then just placed below that in a tab view like so: )
TabView(selection: $activeTab) {
EvalViewMHx()
.tag(1)
EvalObjectiveView()
.tag(2)
viewThree(activeTab: $activeTab)
.tag(3)
viewFour(activeTab: $activeTab)
.tag(4)
}
.tabViewStyle(PageTabViewStyle())
.onChange(of: activeTab, perform: { value in
print(activeTab)
})
.animation(.default)
and the output shows that one of the cells has a totally different height.
Even stranger, another section has a cell with a 0 height but still shows the text field sitting over the rest of the cells. I have a picker view that is also in these cells and putting the pickerStyle to
.pickerStyle(InlinePickerStyle())
expands the cell to larger than an iPad screen size and doesn't put it in a wheel like it did in the previous versions.
Are these bugs that I should just be patient for a fix, or is there something that I can do to fix this and keep moving with my project?
Was struggling with the same issue. Turned out .animation caused the issue, as its deprecated since iOS 15. Removing that line fixed the issue for me.

How to create a 2 column grid with square shaped cells in SwiftUI

I'm trying to replicate this UI in SwiftUI using a Grid.
I created the cell like this.
struct MenuButton: View {
let title: String
let icon: Image
var body: some View {
Button(action: {
print(#function)
}) {
VStack {
icon
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
Text(title)
.foregroundColor(.black)
.font(.system(size: 20, weight: .bold))
.multilineTextAlignment(.center)
.padding(.top, 10)
}
}
.frame(width: 160, height: 160)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.fr_primary, lineWidth: 0.6))
}
}
And the Grid like so.
struct LoginUserTypeView: View {
private let columns = [
GridItem(.flexible(), spacing: 20),
GridItem(.flexible(), spacing: 20)
]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 30) {
ForEach(Menu.UserType.allCases, id: \.self) { item in
MenuButton(title: item.description, icon: Image(item.icon))
}
}
.padding(.horizontal)
.padding()
}
}
}
But on smaller screens like the iPod, the cells are overlapped.
On bigger iPhone screens, still the spacing is not correct.
What adjustments do I have to make so that in every screen size, the cells would show in a proper square shape and equal spacing on all sides?
MenuButton has fixed width and height, thats why it behaves incorrectly.
You could utilise .aspectRatio and .frame(maxWidth: .infinity, maxHeight: .infinity) for this:
struct MenuButton: View {
let title: String
let icon: Image
var body: some View {
Button(action: {
print(#function)
}) {
VStack(spacing: 10) {
icon
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 60, maxHeight: 60)
Text(title)
.foregroundColor(.black)
.font(.system(size: 20, weight: .bold))
.multilineTextAlignment(.center)
}
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.aspectRatio(1, contentMode: .fill)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color. fr_primary, lineWidth: 0.6))
}
}

Add new item in Scroll View in SwiftUI

I'm trying to add a new view inside the Scroll View that contains a button every time that I click in the blue button in the bottom
]1
Here i create the scroll view with 2 buttons, and want to add more after I click in the button on the right
HStack{
ScrollView(.horizontal, content: {
HStack{
PageStep()
PageStep()
}
})
Button(action: {
self.addNewStep = true
}) {
Image(systemName: "plus.square")
.frame(width: 75, height: 75)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 5)
)
}.buttonStyle(PlainButtonStyle()).padding(.trailing, 10)
}
.padding(.leading, 10)
.frame(minWidth: 0, maxWidth: UIScreen.main.bounds.width, minHeight: 80, alignment: .bottom)
.foregroundColor(Color.blue)
struct PageStep: View {
var stepPossition = String()
var body: some View {
Button(action: {
print("Entrou")
}){
Rectangle()
.fill(Color.red)
.frame(width: 75, height: 75)
.cornerRadius(10)
}
}
}
Here is possible approach. Tested with Xcode 11.4.
struct ContentView: View {
#State private var steps = 2 // pages counter
var body: some View {
HStack{
ScrollView(.horizontal, content: {
HStack{
// create available pages
ForEach(0..<steps, id: \.self) { i in
PageStep(stepPossition: "\(i)").id(i) // inject
}
}
})
Button(action: {
self.steps += 1 // << add next page
}) {
Image(systemName: "plus.square")
.frame(width: 75, height: 75)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 5)
)
}.buttonStyle(PlainButtonStyle()).padding(.trailing, 10)
}
.padding(.leading, 10)
.frame(minWidth: 0, maxWidth: UIScreen.main.bounds.width, minHeight: 80, alignment: .bottom)
.foregroundColor(Color.blue)
}
}
struct PageStep: View {
var stepPossition: String
var body: some View {
Button(action: {
print("Entrou")
}){
Rectangle()
.fill(Color.red)
.frame(width: 75, height: 75)
.cornerRadius(10)
}
}
}

SwiftUI - Button with Image is clickable outside

I have a ScrollView with multiple Buttons. A Button contains a Image and a Text underneath.
As the images are pretty large I am using .scaledToFill and .clipped. And it seems that the 'clipped' part of the image is still clickable even if it's not shown.
In the video you see I am clicking on button 1 but button 2 is triggered.
This is my Coding. The Image is inside the View Card.
struct ContentView: View {
#State var useWebImage = false
#State var isSheetShowing = false
#State var selectedIndex = 0
private let images = [
"https://images.unsplash.com/photo-1478368499690-1316c519df07?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2706&q=80",
"https://images.unsplash.com/photo-1507154258-c81e5cca5931?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2600&q=80",
"https://images.unsplash.com/photo-1513310719763-d43889d6fc95?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80",
"https://images.unsplash.com/photo-1585766765962-28aa4c7d719c?ixlib=rb-1.2.1&auto=format&fit=crop&w=2734&q=80",
"https://images.unsplash.com/photo-1485970671356-ff9156bd4a98?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80",
"https://images.unsplash.com/photo-1585607666104-4d5b201d6d8c?ixlib=rb-1.2.1&auto=format&fit=crop&w=2700&q=80",
"https://images.unsplash.com/photo-1577702066866-6c8897d06443?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2177&q=80",
"https://images.unsplash.com/photo-1513809491260-0e192158ae44?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2736&q=80",
"https://images.unsplash.com/photo-1582092723055-ad941d1db0d4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2700&q=80",
"https://images.unsplash.com/photo-1478264635837-66efba4b74ba?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjF9&auto=format&fit=crop&w=2682&q=80"
]
var body: some View {
NavigationView {
ScrollView {
VStack(spacing: 40) {
Text(useWebImage ? "WebImage is used." : "SwiftUI Image is used")
.font(.system(size: 18))
.bold()
.kerning(0.5)
.padding(.top, 20)
Toggle(isOn: $useWebImage) {
Text("Use WebImage")
.font(.system(size: 18))
.bold()
.kerning(0.5)
.padding(.top, 20)
}
ForEach(0..<images.count) { index in
Button(action: {
self.selectedIndex = index
self.isSheetShowing.toggle()
}) {
Card(imageUrl: self.images[index], index: index, useWebImage: self.$useWebImage)
}
.buttonStyle(PlainButtonStyle())
}
}
.padding(.horizontal, 20)
.sheet(isPresented: self.$isSheetShowing) {
DestinationView(imageUrl: self.images[self.selectedIndex], index: self.selectedIndex, useWebImage: self.$useWebImage)
}
}
.navigationBarTitle("Images")
}
}
}
struct Card: View {
let imageUrl: String
let index: Int
#Binding var useWebImage: Bool
var body: some View {
VStack {
if useWebImage {
WebImage(url: URL(string: imageUrl))
.resizable()
.indicator(.activity)
.animation(.easeInOut(duration: 0.5))
.transition(.fade)
.scaledToFill()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
.cornerRadius(12)
.clipped()
} else {
Image("image\(index)")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
.cornerRadius(12)
.clipped()
}
HStack {
Text("Image #\(index + 1) (\(useWebImage ? "WebImage" : "SwiftUI Image"))")
.font(.system(size: 18))
.bold()
.kerning(0.5)
Spacer()
}
}
.padding(2)
.border(Color(.systemRed), width: 2)
}
}
Do you have an idea how to fix this issue?
I already tried to use .resizable(resizingMode: .tile) but I need to shrink the image before I could use just a tile.
For detailed information you can also find the project on GitHub GitHub Project
I would appreciate your help a lot.
The .clipped affects only drawing, and by-default Button has all content clickable not depending what it is.
So if you want make your button clickable only in image area, you have to limit hit testing only to its rect explicitly and disable everything else.
Here is a demo of possible approach. Tested with Xcode 11.4 / iOS 13.4.
Demo code (simplified variant of your snapshot):
struct ButtonCard: View {
var body: some View {
VStack {
Image("sea")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
.cornerRadius(12)
.contentShape(Rectangle()) // << define clickable rect !!
.clipped()
HStack {
Text("Image #1")
.font(.system(size: 18))
.bold()
.kerning(0.5)
Spacer()
}.allowsHitTesting(false) // << disable label area !!
}
.padding(2)
.border(Color(.systemRed), width: 2)
}
}
struct TestClippedButton: View {
var body: some View {
Button(action: { print(">> tapped") }) {
ButtonCard()
}.buttonStyle(PlainButtonStyle())
}
}

SwiftUI - Does adding a shadow mess up interactivity on TextFields?

I am creating a simple SwiftUI view for a Catalyst Mac app like so:
var body: some View {
ZStack {
Color(envColor.getColor())
HStack {
Spacer()
VStack {
HStack {
TextField("First", text: $envColor.stringR)
TextField("Second", text: $envColor.stringG)
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 200)
.background(Color.gray)
Spacer()
VStack {
Text("Right Side")
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 200)
Spacer()
}
}
}
When I run the App it just looks like this:
A simple view with 2 text boxes. Both of them you can freely type into without issue.
You can highlight and edit either 255 without issue.
However, when I add a shadow to my VStack like so:
var body: some View {
ZStack {
Color(envColor.getColor())
HStack {
Spacer()
VStack {
HStack {
TextField("First", text: $envColor.stringR)
TextField("Second", text: $envColor.stringG)
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 200)
.background(Color.gray)
.shadow(radius: 5)
Spacer()
VStack {
Text("Right Side")
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 200)
Spacer()
}
}
}
The app looks exactly the same but I can't type in the TextFields at all. They don't highlight and I can't type in them. I looked at the Debug View Hierarchy and it looks fine with the TextFields in the front.
Here's a video of me using it with the shadow. As you can see the cursor doesn't change to let me edit.
Does adding a shadow to a VStack actually cause issue? And I doing something incorrect? Or is this a bug?
Probably this is a defect - you can submit feedback to Apple. Meanwhile I can propose solution - put shadow into background:
Tested with Xcode 11.4 / macOS 10.15.4
VStack {
HStack {
TextField("First", text: $first)
TextField("Second", text: $second)
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 200)
.background(Color.gray.shadow(radius: 5)) // << here !!
Just add clipped() and it will work:
var body: some View {
HStack {
TextField(
placeholder,
text: $text,
onEditingChanged: {
isEditing = $0
}
)
....
}
.background(Colors.shared.background.primary)
.clipped()
.shadow(color: .black.opacity(0.3), radius: 0, x: 0, y: 0.33)
.padding(.bottom, 1)
}
The following change to my code solved the problem in addition to #Asperi's solution. I added .clipped() and now the TextFields work.
var body: some View {
ZStack {
Color(envColor.getColor())
HStack {
Spacer()
VStack {
HStack {
TextField("First", text: $envColor.stringR)
TextField("Second", text: $envColor.stringG)
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 200)
.clipped()
.background(Color.gray)
Spacer()
VStack {
Text("Right Side")
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 100, maxHeight: 200)
Spacer()
}
}
}

Resources