I have some code that was working in iOS 14, but in 15, isn’t quite. The cells pictured below can have one or two lines of title. In iOS 14 (on the right), the Spacer() element between the title and the listing count text expands as expected so that the cells are all the same height. In iOS 15, it does not, and the cell shrinks, and gets centered in its row (see the “A-Train” item).
The code for the cell looks like:
ZStack
{
Color.white
.cornerRadius(8)
VStack(alignment: .leading, spacing: 0)
{
// Listing Image…
GeometryReader
{ geom in
KFImage(self.imageURL)
.cancelOnDisappear(true)
.placeholder {
Image("product-placeholder")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: geom.size.width, height: geom.size.width)
}
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geom.size.width, height: geom.size.width)
.cornerRadius(4)
.clipped()
}
.aspectRatio(contentMode: .fit)
.padding(.bottom, 12)
// Title…
Text("\(self.title)")
.font(.custom("Poppins", size: 14.0).weight(.semibold))
.multilineTextAlignment(.leading)
.lineLimit(2)
Spacer(minLength: 0) // Not expanding in iOS 15
// Listing Count…
if let listingCount = self.listingCount
{
Text("\(listingCount) listings")
.font(.custom("Inter", size: 12.0).weight(.medium))
.foregroundColor(.secondaryText)
.padding(.vertical, 8)
}
// Price…
if let price = self.price
{
Text("\(Self.priceFormatter.string(from: price as NSNumber)!)")
.font(.custom("Inter", size: 14.0).weight(.semibold))
}
}
.padding(12)
}
The columns for the LazyVGrid is Array(repeating: .init(.flexible(), spacing: 20.0), count: 2).
I can’t tell if iOS 14 or 15 has the bug.
I am having exactly the same issue here. A possibility is to work around it, at least if you are using a LazyVGrid or VGrid. This can be done by aligning the grid items to the top.
Array(repeating: .init(.flexible(), spacing: 20.0), count: 2, alignment: .top)
I am aware this will not fully fix the issue, but it will align them at the top, not centered.
Related
I would like the Image "Astronaut Meditation (Traced)" and the text "Levitate" to be aligned as in the prototype image, which is in the dimensions of an iPhone 13 pro max. How I want it to look
Here is my ContentView code:
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack
{
//Background
Image("Background")
.edgesIgnoringSafeArea([.top])
**//Meditating Astronaut
Image("Astronaut Meditaton (Traced)")
.position(x: 102, y: 106)
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
.foregroundColor(Color.white)
.position(x: 110, y: 386)**
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I have included an image of how it is previewed on Xcode:
How it looks in XCode
Do not use static value alignment like position or offset because it’s not stable for all devices, instead just wrap your content inside VStack{} then you will achieve your desired design.
ZStack {
//Background
Image("Background")
.edgesIgnoringSafeArea([.top])
VStack { //start wrapping after background color image
//Meditating Astronaut
Image("Astronaut Meditaton (Traced)")
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
.foregroundColor(Color.white)
}
}
Like #tail said, you should avoid absolute position & offset, since it will vary from device to device. But if you do want to use them for some reason, you can add a alignment parameter to the ZStack and then use offset to get the desired outcome.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack(alignment: .center) {
//Background
Image("Background")
.ignoresSafeArea(edges: [.top])
//Meditating Astronaut
Image(systemName: "gear")
.resizable()
.frame(width: 150, height: 150)
.offset(y: -150)
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
.offset(y: -50)
}
}
}
Even better way would be to use GeometryReader and work with relative values to the screen size:
import SwiftUI
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .center) {
//Background
Image("Background")
.ignoresSafeArea(edges: [.top])
//Meditating Astronaut
Image(systemName: "gear")
.resizable()
.frame(width: 150, height: 150)
.position(x: geometry.size.width / 2, y: geometry.size.height / 5)
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
// Position with offsetting by the size of image.
// Image frame 150 -> half is 75 + 25 for padding.
.position(x: geometry.size.width / 2, y: geometry.size.height / 5 + 100)
}
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
With GeometryReader I would use absolute position and calculate the position of image with the relative values and then use the image size in addition to position the text
Use below code to achieve your actual desire design. Wrap your content in VStack and also add frame to background image for bind in your whole device with device width and height.
And also add Spacer() in VStack to occupy remains bottom space(For move view to up side).
ZStack {
//Background
Image("Background")
.resizable()
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height) //Here add frame to background image
.edgesIgnoringSafeArea(.all)
VStack { //Wrap your view in VStack
//Meditating Astronaut
Image("Astronaut Meditaton (Traced)")
.resizable()
.frame(width: 100, height: 100)
.padding(.top,20)
//Levitate
Text("Levitate")
.font(.system(size: 34, weight: .semibold, design: .rounded))
.foregroundColor(Color.white)
Spacer() //Add Spacer to move view to upper side.
}.padding(.top,UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0 + 150) //Add padding with safe area to top side for upper space(suitable for all devices).
}
I am currently learning SwiftUI and I followed this video to create a custom NavigationBar.
Learn how to create a custom navigation bar with a logo in SwiftUI framework and Xcode - Part 2
The buttons doesn't work when I click on it unless I don't use padding() and ignoreSafeArea(), but without using it, the navigationBar would appear on middle of the page. (I also tried use Spacer() but it didn't work)
Is there anyway to fix this issue?
Code for HomePage()
VStack{
NavigationBarView()
.padding(.horizontal, 15)
.padding(.bottom)
.padding(.top, UIApplication.shared.windows.first?.safeAreaInsets.top)
.shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 5)
.ignoresSafeArea(.all, edges: .top)
Code for NavigationBarView()
HStack{
NavigationLink(
destination: PersonalMenuPage()
,label: {
ZStack {
Image("Profile")
.renderingMode(.original)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 40, height: 40)
.padding(.horizontal, 10)
Circle()
.fill(Color.red)
.frame(width:14, height: 14, alignment: .center)
.offset(x: 13, y: -10)
}
}
)
}//: hStack
Here is sample output
Sample output
I have a horizontal ScrollView and would like to provide a function that when the first element is not visible anymore an arrow should appear to signalize that there are some other elements.
I've tried the DragGesture() to read the translation.width but it seems to be buggy in combination with the ScrollView.
So I'm looking for a way to detect whether the ScrollView was scrolled and how far. Is there any way?
private let gridItems = [GridItem(.flexible())]
ScrollView(.horizontal, showsIndicators: false) {
HStack{
LazyHGrid(rows: gridItems){
ForEach(viewModel.imageOptions, id: \.self){ image in
ZStack {
Image(image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
.background(Color.white)
.clipShape(Circle())
.background(
Circle()
.clipShape(Circle())
.shadow(color: Color.black.opacity(0.4), radius: 3, x: 5.0, y: 5.0)
)
.onTapGesture {
print("selected")
}
.padding(15)
Circle()
.strokeBorder(Color.orange, lineWidth: 5)
.frame(width: 70, height: 70)
}
}
}
}
}
}
I am trying to clip the image and as we see the UI it looks fine but actually it doesn't clip the image which causes other UI elements to unresponsive.
Here the code I am using.
struct ImageContentView: View {
var urls:[String] = [
"https://lh3.googleusercontent.com/proxy/80im-IBfLODpLDj8d02uEpSVIhqdjen6H6CeFwgRBxeua-Dgw0R3WONFj1Gk8CwB_MufmC9rQ8qHjyWMejwFcJ1PA2s8AAu5WVsmJA=s0-d",
"https://wallpaperaccess.com/full/530919.jpg"
]
var body: some View {
ScrollView{
VStack{
Button(action: {
}, label: {
Text("Hello")
})
VStack(spacing: 20.0) {
ForEach(self.urls, id:\.self) { url in
WebImage(url: URL.init(string: url)!)
.resizable()
.aspectRatio(contentMode: ContentMode.fill)
.frame(height: UIScreen.main.bounds.size.width * 0.5)
.clipped()
.cornerRadius(10.0)
.shadow(color: Color.red, radius: 10.0, x: 0, y: 0)
}
}.padding()
}
}
}
}
Here is fixed part (tested with Xcode 12 / iOS 14)
VStack(spacing: 20.0) {
ForEach(self.urls, id:\.self) { url in
WebImage(url: URL.init(string: url)!)
.resizable()
.aspectRatio(contentMode: ContentMode.fill)
.frame(height: UIScreen.main.bounds.size.width * 0.5)
.clipped()
.cornerRadius(10.0)
.shadow(color: Color.red, radius: 10.0, x: 0, y: 0)
}.contentShape(Rectangle()) // << here !!
}.padding()
Note: I don't know what is your WebImage but with Image and local images it was reproduced as well, so fix was tested.
iOS 13 and +
An other solution is combine compositingGroup and clipped:
That wraps this view in a compositing group and clips.
Note: Respect this order compositingGroup and clipped
Image(..)
.compositingGroup()
.clipped()
I have tried for the above methods but still not working.
Here is another solution for someone who have the same struggle, maybe this can help you :) This line is to disable the tap gesture of the image.
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.contentShape(RoundedRectangle(cornerRadius: 8))
.allowsHitTesting(false) // <- this is the line
I'm trying to add a User Image (button) next to the .navigationBarTitle, but with the code bellow, the image appears on top of the title alignment. (picture attached). Many thanks for your help!
.navigationBarTitle(Text("Watch"))
.navigationBarItems(trailing:
Image("User")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 36, height: 36)
.clipShape(Circle())
)
Image should be bottom - aligned to the text
This code produces this view:
struct ContentView: View {
var body: some View {
NavigationView {
List {
Text("Chocolate")
Text("Vanilla")
Text("Strawberry")
}.navigationBarTitle(Text("Watch"))
.navigationBarItems(trailing:
Image(systemName: "person.circle")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 36, height: 36)
.clipShape(Circle())
.padding(.top, 90)
)
}
}
}
There is a default space for the Items and a default space for the Text. Imagine it like two HStacks in a VStack. Where the Title is in the lower HStack and the items are in the upper one. There is no "real" way on getting in the lower one.
I'd recommend to create an own NavigationBar for your purposes.
Thanks #Simon, the best option for what I was looking for is to Add the User Icon to the Title (not NavBar) and apply an Offset of y: -55. When scrolling up the icon disappears under the NavBar. The same effect on the Apple TV app (mobile).` VStack(alignment: .leading) {
HStack {
Text("Children")
.font(.title)
.fontWeight(.bold)
.padding(.leading, 24)
.padding(.top, 20)
Spacer ()
Image("User")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 36, height: 36)
.clipShape(Circle())
.offset(y: -55)
.padding(.trailing, 24)[final result][1]`