The images in my 'tiles' are being cut off at the sides. I am trying to create a tile for each 'product' that displays an image, name and subtitle. Everything now works as it should besides the image. This is because the images on the tiles are being cut off at the sides.
The view for the ContentView is as follows:
ScrollView(.vertical, showsIndicators: false, content: {
Spacer()
LazyVGrid(columns: Array(repeating: GridItem(.flexible(),spacing: 15), count: 2),spacing: 10){
ForEach(HomeModel.filteredProduct){product in
// Product View...
ProductView(productData: product)
.background(.white)
.clipShape(RoundedRectangle(cornerRadius: 15, style: .continuous))
.shadow(color: .black.opacity(0.4), radius: 3, x: 1, y: 1)
.onTapGesture {
withAnimation(.easeIn){
selectedProduct = charity
show.toggle()
}
}
}
}
Spacer()
.padding()
.padding(.top,10)
})
.padding(.top, 20)
And the ProductView code is as follows:
var body: some View {
VStack(alignment: .center, spacing: 0) {
WebImage(url: URL(string: productData.product_image))
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 160, height: 225)
.cornerRadius(15)
.clipped()
.padding(5)
Text(productData.product_name)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.black)
.padding()
Text(productData.product_type)
.font(.caption)
.foregroundColor(.gray)
.lineLimit(2)
.padding()
}
}
Here is an image showing what is meant by 'the tile is cutting it off':
As you can see in the photo, the image is not all included because it doesn't fit in the tile. This needs to be resized.
The first issue here would be the .contentMode modifier it needs to be set to fill. If you have an image where the width is larger then the height but you set the frame at a different aspect ratio you will see a white space above and below the image.
But there were other problems too. You need to clip an image with the .clipped() modifier after you set its frame, else it will overflow the frame. Why all those ZStacks? I cannot see any purpose of adding a ZStack with only one child.
BUT:
As your code is not reproducible i had to add or remove several things to make it work. So you need to adopt this to your code.
Solution:
struct ContentView: View{
#State private var products: [ProductData] = [ProductData(product_name: "test", product_details: "test"), ProductData(product_name: "test2", product_details: "test2"), ProductData(product_name: "test3", product_details: "test3")]
#State private var show = false
#State private var selectedProduct: ProductData?
var body: some View{
ScrollView(.vertical, showsIndicators: false, content: {
Spacer()
LazyVGrid(columns: Array(repeating: GridItem(.flexible(),spacing: 15), count: 2),spacing: 10){
ForEach(products){product in
// Product View...
ProductView(productData: product)
.background(.white)
.clipShape(RoundedRectangle(cornerRadius: 15, style: .continuous))
.shadow(color: .black.opacity(0.4), radius: 3, x: 1, y: 1)
.onTapGesture {
withAnimation(.easeIn){
selectedProduct = product
show.toggle()
}
}
}
}
Spacer()
.padding()
.padding(.top,10)
})
.padding(.top, 20)
}
}
struct ProductData: Identifiable{
var id = UUID()
var product_name: String
var product_details: String
}
struct ProductView: View{
var productData: ProductData
var body: some View{
VStack(alignment: .center, spacing: 0) {
Image("test")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 160, height: 225)
.cornerRadius(15)
.clipped()
.padding(5)
Text(productData.product_name)
.font(.title2)
.fontWeight(.bold)
.foregroundColor(.black)
.padding()
Text(productData.product_details)
.font(.caption)
.foregroundColor(.gray)
.lineLimit(2)
.padding()
}
}
}
Outcome:
Related
I am trying to create a messaging page layout but the keyboard hides the textfield and moves everything to the the top. I have looked at many tutorials online and replicated them exactly but none of them seem to work.
Here is all the code I have written:
struct MessagingPage: View {
var user: OfficialUserModel
#ObservedObject private var vm = TheUserModel()
#State var screenWidth = UIScreen.main.bounds.width
#State var imageSize = UIScreen.main.bounds.width / 12
#State var text = ""
#State var show = false
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView{
ZStack{
VStack{
HStack{
Spacer()
NavigationLink(destination: UserDisplayPage(user: user)) {
HStack{
WebImage(url: URL(string: user.imageURL ))
.resizable()
.aspectRatio( contentMode: .fill)
.frame(width: imageSize, height: imageSize
)
.cornerRadius(imageSize/2)
VStack(alignment: .leading){
Text(user.FullName)
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
Text("\(user.City) , \(user.Country)")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.white)
}
}
}
Spacer()
HStack{
Button{
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "chevron.down")
.font(.title)
.foregroundColor(.myWhite)
}
}
.padding(.trailing)
}
.padding(.top, 30)
.frame(height: 75)
.background(Color.mainBlack)
ScrollView{
Spacer()
ForEach(0..<2){ num in
HStack{
Spacer()
HStack{
Text("Hello \(user.firstName)")
.foregroundColor(.orange)
}
.padding()
.background(Color.mainBlack)
.cornerRadius(15)
.shadow(color: .orange, radius: 2)
}
.padding(.horizontal)
.padding(.top, 8)
}
HStack{Spacer()}
HStack{
HStack{
Text("\(user.FullName) is unable to recieve your message at this time. Please try again at a later time.")
.foregroundColor(.green)
}
.padding()
.background(Color.mainBlack)
.cornerRadius(15)
.shadow(color: .green, radius: 2)
Spacer()
}
.padding(.horizontal)
.padding(.top, 8)
HStack{Spacer()}
}
.background(Color.mainBlack)
ZStack{
HStack{
HStack{
HStack{
TextField("Say Something...", text: self.$text)
.placeholder(when: text.isEmpty) {
Text("Say Something...").foregroundColor(.myWhite.opacity(0.5))
}
.frame(width: screenWidth - 200, height: screenWidth/25)
.foregroundColor(.myCyan)
.accentColor(.myCyan)
.background(Color.mainBlack)
.textContentType(.emailAddress)
if !text.isEmpty{
Button{
print(text)
self.text = ""
}label: {
Image(systemName: "paperplane")
.foregroundColor(.myCyan)
.font(.system(size: 20))
}
}
else{
Button{
print("Show more options")
}label: {
Image(systemName: "plus")
.foregroundColor(.myCyan)
.font(.system(size: 20))
}
}
}
.frame(width: screenWidth - 150, height: screenWidth/25)
.padding()
.background(Color.mainBlack)
.cornerRadius(30)
.shadow(color: .myCyan, radius: 5)
.padding(.bottom,5)
}
}
.padding(.bottom, 50)
.frame(width: screenWidth)
}
}
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.background(Color.mainBlack)
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
I want the the textfield to move up as well as the messages in the scrollview but the top HStack with the user image and back/dismiss button should remain in place.
You really have a lot of code in there - and two reasons to clean it up:
To get a good answer, you need to post a minimal, reproducible example, which may also help you debug the code before posting it
Many lines of code are redundant or useless, for example:
Why using things like HStack{Spacer()} instead of Spacer(), which by the way is not needed in this context?
Why having a stack that has only another stack inside?
Coming to your issue, the problem is that one of the redundant modifiers is preventing the text file to move up, and this is the line to delete:
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
Why setting a frame that is as high and large as much as the screen? This is forcing the text field to stay at the bottom.
I tried to do some clean up, I bet some more can be done - see the code below, now the keyboard does not cover the text field anymore:
struct MessagingPage: View {
// Removed some lines of code to reproduce your issue
#State var screenWidth = UIScreen.main.bounds.width
#State var imageSize = UIScreen.main.bounds.width / 12
#State var text = ""
#State var show = false
#Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView{
// What is the purpose of this ZStack???
// ZStack{
VStack{
HStack{
Spacer()
NavigationLink(destination: Text("Hello")) {
Text("Top bar")
}
Spacer()
HStack{
Button{
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "chevron.down")
.font(.title)
.foregroundColor(.white)
}
}
.padding(.trailing)
}
.padding(.top, 30)
.frame(height: 75)
.background(Color.black)
ScrollView{
// All the views inside the Scrollview need to be inside a VStack
VStack {
// Why this spacer???
// Spacer()
ForEach(0..<2){ num in
HStack{
Spacer()
HStack{
Text("Hello username")
.foregroundColor(.orange)
}
.padding()
.background(Color.black)
.cornerRadius(15)
.shadow(color: .orange, radius: 2)
}
.padding(.horizontal)
.padding(.top, 8)
}
HStack{Spacer()}
HStack{
HStack{
Text("User is unable to receive your message at this time. Please try again at a later time.")
.foregroundColor(.green)
}
.padding()
.background(Color.black)
.cornerRadius(15)
.shadow(color: .green, radius: 2)
Spacer()
}
.padding(.horizontal)
.padding(.top, 8)
// Why a Spacer inside an HStack???
// HStack{Spacer()}
}
.background(Color.black)
}
// What is the purpose of this ZStack???
// ZStack{
// Why an HStack that has only an HStack inside??
// HStack{
// Why an HStack that has only an HStack inside??
// HStack{
HStack{
TextField("Say Something...", text: self.$text)
.frame(width: screenWidth - 200, height: screenWidth/25)
.foregroundColor(.cyan)
.accentColor(.cyan)
.background(Color.white)
.textContentType(.emailAddress)
if !text.isEmpty{
Button{
print(text)
self.text = ""
}label: {
Image(systemName: "paperplane")
.foregroundColor(.cyan)
.font(.system(size: 20))
}
}
else{
Button{
print("Show more options")
}label: {
Image(systemName: "plus")
.foregroundColor(.cyan)
.font(.system(size: 20))
}
}
}
// Are you sure you want set the height of this stack based on the width?
// .frame(width: screenWidth - 150, height: screenWidth/25)
.frame(width: screenWidth - 150)
.padding()
.background(Color.black)
.cornerRadius(30)
.shadow(color: .black, radius: 5)
.padding(.bottom,5)
// }
// }
.padding(.bottom, 50)
.frame(width: screenWidth)
// }
}
// }
// Why setting a frame that is high and large as much as the screen??
// .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.background(Color.black)
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
I am seeing an odd crash with some beta testers on TestFlight. It seems to be at app startup, but prior to the point where we are making any network calls. There does not seem to be a lot on the internet regarding this, and symbolication isn't helping much either. Just curious if anyone has run into this before and what they did about it? I have had it happen on two tester's devices and I have never been able to recreate the issue locally.
For a bit of context, I am creating a list view on my main screen with a LazyVGrid that contains a LazyHGrid inside of it that are both filled using published vars from my viewmodel, though this seems to happen prior to any of those being created.
Thanks for any ideas / help
Edit: Some more details / actual code:
The view with the grids:
import SwiftUI
import Kingfisher
struct FeaturedView: View {
#EnvironmentObject var viewRouter: ViewRouter
#EnvironmentObject var tabRouter : TabRouter
#StateObject var loadingViewModel = LoadingViewModel()
private let imageProcessor = SVGImgProcessor()
private let playerManager = PlayerManager.shared
private var gridItemLayout = [GridItem(.flexible())]
let userDefaults = UserDefaults.standard
var body: some View {
let padding: CGFloat = 20
let paddingHStack: CGFloat = 25
GeometryReader { geometry in
ZStack(alignment: .top){
Color(hex:"#00091C").edgesIgnoringSafeArea(.all)
VStack {
HStack {
HStack {
Text("Hello, \(loadingViewModel.name)")
.frame(alignment: .leading)
.multilineTextAlignment(.center)
.font(Font.custom("poppins-medium", size: 20))
.foregroundColor(Color(hex:"#667C95"))
.padding(.leading, 15)
.padding(.top, 15)
.padding(.bottom, 15)
Image("PremiumStar")
.resizable()
.frame(width: 15.0, height: 15.0)
.opacity(userDefaults.isSubscriber() ? 1 : 0)
}
Spacer()
Button(action: {
print("Settings Clicked")
viewRouter.currentPage = .settingsFlow
}) {
Image("Settings")
.resizable()
.frame(width: 22.0, height: 22.0)
.padding(15)
}
}
ScrollView(showsIndicators: false) {
VStack(spacing: 10) {
LazyVGrid(columns: gridItemLayout, spacing: 17) {
ForEach(loadingViewModel.getCategories()) { category in
Text(category.title)
.foregroundColor(.white)
.font(Font.custom("poppins-bold", size: 30))
ZStack {
KFImage(URL(string: (category.background?.svg!)!))
.resizable()
.setProcessor(imageProcessor)
.frame(width: geometry.size.width - padding, height: 175, alignment: .topLeading)
.aspectRatio(contentMode: .fill)
.clipped()
.cornerRadius(5)
ScrollView(.horizontal, showsIndicators: false) {
LazyHGrid(rows: gridItemLayout, spacing: 20){
ForEach(loadingViewModel.getSoundsForCategory(category: category.key)) { sound in
Button(action: {
playerManager.play(sound: sound)
}) {
VStack {
ZStack{
RoundedRectangle(cornerRadius: 5).frame(width: 90, height: 90)
.foregroundColor(Color.black)
.opacity(0.85)
ZStack(alignment:.bottomTrailing){
KFImage(URL(string: sound.icon.png!)!)
.resizable()
.renderingMode(.template)
.foregroundColor(.white)
.frame(width: 75, height: 75)
.aspectRatio(contentMode: .fill)
.clipped()
Image("LockIcon")
.frame(width: 12, height: 12, alignment: .bottomTrailing)
.aspectRatio(contentMode: .fill)
.clipped()
.hidden(loadingViewModel.isSubscriber || sound.tier != 2)
}
}
Text(sound.name)
.foregroundColor(.white)
.font(Font.custom("poppins-regular", size: 12))
.lineLimit(1)
.frame(minWidth: 0, idealWidth: 90, maxWidth: 100, alignment: .center)
}
}
}
}.padding(.horizontal)
}
.frame(width: geometry.size.width - paddingHStack, height: 175, alignment: .topLeading)
}
Button("Explore All"){
print("Explore All \(category.title) Tapped")
tabRouter.categoryKey = category.key
tabRouter.hasChanged = true
tabRouter.currentTab = 1
}
.font(Font.custom("poppins-bold", size: 15))
.foregroundColor(Color.white)
}
}.padding(.bottom, 120)
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
FeaturedView()
}
}
That is loaded by this tab view, which goes between two screens which share the same viewModel / load the same data
// MARK: Tab View
var body: some View {
ZStack{
TabView(selection: $selectedTab){
FeaturedView()
.environmentObject(tabRouter)
.tabItem{
Label("FEATURED", image: "Featured")
}.tag(0)
.overlay(
VStack {
if (showOverlay && !isExpanded){
playView
} else {
EmptyView()
}
}
)
SoundView()
.environmentObject(tabRouter)
.tabItem{
Label("SOUNDS", image: "Sounds")
}.tag(1)
.overlay(
VStack {
if (showOverlay && !isExpanded){
playView
} else {
EmptyView()
}
}
)
}
}
}
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))
}
}
I am trying to build an app in SwiftUI and facing 1 challenge (Xcode Version 11.5) -
While running app on iPhone 11 simulator, background color is not coming on entire screen, bottom part of screen is still white however while running it on iPhone 8 simulator, it works fine. Not sure if it is simulator issue or code issue. I tried to add Spacer, change VStack, HStack but it did not work.
struct HomePageView: View {
#State var size = UIScreen.main.bounds.width / 1.6
var body: some View {
GeometryReader{geometry in
VStack {
HStack {
ZStack{
NavigationView{
ZStack {
ScrollView(.vertical, showsIndicators: false) {
VStack {
View1()
}.frame( maxWidth: .infinity)
}
.navigationBarItems(leading: Button(action: {
self.size = 10
}, label: {
Image("menu")
.resizable()
.frame(width: 30, height: 30)
}).foregroundColor(.appHeadingColor), trailing:
Button(action: {
print("profile is pressed")
}) {
HStack {
NavigationLink(destination: ProfileView()) {
LinearGradient.lairHorizontalDark
.frame(width: 30, height: 30)
.mask(
Image(systemName: "person.crop.circle")
.resizable()
.scaledToFit()
)
}
}
}
).navigationBarTitle("Home", displayMode: .inline)
}
}
HStack{
menu(size: self.$size)
.cornerRadius(20)
.padding(.leading, -self.size)
.offset(x: -self.size)
Spacer().background(Color.lairBackgroundGray)
}
//Spacer()
}.animation(.spring()).background(Color.lairBackgroundGray)
//Spacer()
}.padding(.top, UIApplication.shared.windows.first?.safeAreaInsets.top)
.padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
}.frame(height: geometry.size.height).background(Color.lairBackgroundGray)
}//.background(Color.lairBackgroundGray.edgesIgnoringSafeArea(.all))
}
}
Below is my another view which basically get painted on screen as part of home view. Please forgive me to put so much code here but wanted to make sure if it is not because of View1 -
struct View1: View {
#State var index = 0
var body: some View{
// ScrollView {
GeometryReader { geometry in
VStack{
HStack{
VStack {
ZStack{
Circle()
.trim(from: 0, to: 1)
.stroke(Color.lairDarkGray.opacity(0.09), style: StrokeStyle(lineWidth: 34, lineCap: .round))
.frame(width: 80, height: 80)
Circle()
.trim(from: 0, to: 0.5)
.stroke(LinearGradient(gradient: Gradient(colors: [.buttonGradientStartColor, .buttonGradientEndColor]), startPoint: UnitPoint(x: -0.2, y: 0.5), endPoint: .bottomTrailing), style: StrokeStyle(lineWidth: 34, lineCap: .round))
.frame(width: 80 , height: 80)
.rotationEffect(.init(degrees: -90))
Text("15")
.font(.system(size:30))
.fontWeight(.bold)
}.padding()
Text("Day(s)")
.foregroundColor(Color.black.opacity(0.8))
}.frame(height: 100)
VStack(alignment: .leading, spacing: 12){
HStack {
Image("1")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 170, height: 170)
}
.background(Color.lairBackgroundGray)
//.padding(.bottom, 5)
}
.padding(.leading, 20)
Spacer(minLength: 0)
}
.padding(.horizontal, 20)
}//.frame(height: geometry.size.height)
.background(Color.lairBackgroundGray.edgesIgnoringSafeArea(.all))
}
//}
}
}[![enter image description here][1]][1]
You just need to add
.edgesIgnoringSafeArea(.all)
to the view that you want to go fullscreen
After recreating entire view and removing some unnecessary view, i was able to resolve this iissue.
I have a scrollview which content is a VStack containing a ForEach loop to create some Rows instead of a list. A List has some downsides like the dividers.
My Issue is that the Row is not filling the scrollview. I think the scrollview width is not filling the screen.
NavigationView {
Toggle(isOn: $onlineStatus) {
Text("Online Only")
}.padding([.leading, .trailing], 15)
ScrollView {
VStack(alignment: .trailing) {
ForEach(onlineStatus ? self.notes.filter { $0.dot == .green } : self.notes) { note in
NavigationButton(destination: Text("LOL")) {
CardRow(note: note)
.foregroundColor(.primary)
.cornerRadius(8)
.shadow(color: .gray, radius: 3, x: 0, y: -0.01)
}.padding([.leading, .trailing, .top], 5)
}.animation(self.onlineStatus ? .fluidSpring() : nil)
}
}.padding([.leading, .trailing])
.navigationBarTitle(Text("Your documents"))
}
This is giving me this result:
That's my CardRow:
struct CardRow: View {
var note: Note
var body: some View {
HStack {
Image(uiImage: UIImage(named: "writing.png")!)
.padding(.leading, 10)
VStack(alignment: .leading) {
Group {
Text(note.message)
.font(.headline)
Text(note.date)
.font(.subheadline)
}
.foregroundColor(.black)
}
Spacer()
VStack(alignment: .trailing) {
Circle().foregroundColor(note.dot)
.frame(width: 7, height: 7)
.shadow(radius: 1)
.padding(.trailing, 5)
Spacer()
}.padding(.top, 5)
}
.frame(height: 60)
.background(Color(red: 237/255, green: 239/255, blue: 241/255))
}
}
Use .frame(minWidth: 0, maxWidth: .infinity) on the RowView or inside of it
Best solution I found is to use GeometryReader to fill the ScrollView's contents to the outer view's width. And be sure to use a Spacer() in each row's HStack. This handles safe areas and rotation well.
struct Card : View {
var body: some View {
HStack {
VStack(alignment: .leading) {
Text("Header")
.font(.headline)
Text("Description")
.font(.body)
}
Spacer()
}
.padding()
.background(Color.white)
.cornerRadius(8)
.shadow(color: Color.black.opacity(0.15), radius: 8, x: 0, y: 0)
}
}
struct Home : View {
var body: some View {
GeometryReader { geometry in
NavigationView {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Card()
Card()
Card()
}
.padding()
.frame(width: geometry.size.width)
}
.navigationBarTitle(Text("Home"))
}
}
}
}
See resulting screenshot:
Try setting the frame width of the RowView to UIScreen.main.bounds.width:
.frame(width: UIScreen.main.bounds.width)