Main View Code , My Subview CodeMy CardView is in ZStack and, when stretching the background image, didn't select in the right way a GridView in Main View here is my codes of main and subview
ScrollView {
NavigationLink("", destination: CardCategoryView(cardCategoryVm: .init(card: cardsVm.selectedCard)), isActive: $cardsVm.isShowingCardCategory)
LazyVGrid(columns: gridColoumns,spacing: 8) {
ForEach(cardsVm.cards?.vendor ?? [] ,id: \.self) { symbol in
StoreCardView(image: symbol.image ?? "")
.frame(width: UIScreen.main.bounds.width / 2 - 20,height: UIScreen.main.bounds.width / 2 - 20)
.padding(.top)
.onTapGesture {
let _ = print(symbol.name)
cardsVm.selectedCard = symbol
cardsVm.isShowingCardCategory = true
}
}
}
}
struct StoreCardView: View {
var image:String
var body: some View {
GeometryReader { reader in
ZStack{
KFImage(URL(string:image))
.resizable()
.scaleEffect(x: 3, y: 3, anchor: .center)
.aspectRatio(contentMode: .fit)
.frame(width: reader.size.width , height: reader.size.height , alignment: .center)
.clipped()
.blur(radius: 25, opaque: true)
.opacity(0.7)
.cornerRadius(8)
KFImage(URL(string:image))
.resizable()
.aspectRatio( contentMode: .fill)
.frame(width: reader.size.width/2, height: reader.size.height/2, alignment: .center)
.clipShape(Circle())
.shadow(color: .movColor.opacity(0.4),radius: 5,
x: 0,
y: 0
)
//
} .fixedSize(horizontal: true, vertical: true)
.frame(width: reader.size.width, height: reader.size.height, alignment: .center)
.clipped()
}
}
}
I made it in Ztack and gave it a fixed frame with Scale Effect and it didn't work also, how can I make the background image stretching and didn't effect on gridview at main view
Related
Here is my code:
import SwiftUI
struct TrendingView : View {
#State var selectedTab: Tabs = .hot
#State private var searchText = ""
var body: some View {
NavigationView {
VStack {
HStack {
Text(" Trending")
.font(.largeTitle)
.frame(width: 175, height: 50, alignment: .leading)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(.black, lineWidth: 2)
)
.italic()
.fontWeight(.heavy)
.foregroundColor(Color(hue: 1.0, saturation: 0.977, brightness: 0.985))
.offset(x: -75, y: -25)
.padding(.bottom, -20)
NavigationLink(destination: testT()) {
Image(systemName: "magnifyingglass.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 40, height: 40)
.foregroundColor(.black)
.padding(.bottom, -20)
}
.offset(x: 50, y: -25)
.padding(.leading, 5.0)
}
ScrollView {
VStack {
Text("Hot Items 🔥")
.bold()
.italic()
.offset(x: 5)
.frame(width: 120, height: 25, alignment: .leading)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(.black, lineWidth: 2)
)
.background(.yellow)
.offset(x: -130, y: 5)
ScrollView(.horizontal) {
HStack {
NavigationLink(destination: ImageTest()) {
Image("testpic1")
.resizable()
.frame(width: 200, height: 200)
}
NavigationLink(destination: ImageTest()) {
Image("testpic3")
.resizable()
.frame(width: 200, height: 200)
}
}.offset(y: 30)
}
}
}
There seems to be some error with scrollview above my vstack as it randomly causes all the content on this view to shift downwards when switching to and from other views.Ive tried removing the scrollview and the error disappears and also tried changing it around in other places but I still get the error Can anybody point me in the right direction? Thanks
Fixed, added to the bottom of my code and it seems like the issue is solved
.navigationBarTitle(Text(""), displayMode: .inline)
.navigationBarHidden(true)
today I implemented the new UI what you can see here in the red circle on the #Fix image. Before that the screen looked like the #Old Screen without Feature. But with the new UI element I have now a padding from the map to the name of the store. I have tried many things and researched many things, but in the meantime I have been working on it for a week without success.
#FIX Image
#Old Screen (Result should be like this)
The Code
var body: some View {
VStack(spacing: 0) {
shopModel.logo
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 200, maxHeight: 100)
.padding([.top, .leading, .trailing])
Text(shopModel.name)
.fontWeight(.bold)
.padding()
CustomerCardPoints()
ZStack(alignment: .bottomTrailing) {
MapView()
.frame(height: isMapInFront ? 250 : 100)
.frame(maxWidth: isMapInFront ? .infinity : 100 )
.zIndex(isMapInFront ? 0 : 1)
.cornerRadius(isMapInFront ? 0 : 10)
.padding(isMapInFront ? 0 : 10)
.onTapGesture {
if !isMapInFront {
withAnimation {
isMapInFront.toggle()
}
} else {
let url = URL(string: "maps://?saddr=&daddr=\(shopModel.pinLocation.latitude),\(shopModel.pinLocation.longitude)")
if UIApplication.shared.canOpenURL(url!) {
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
}
}
}
The Struct:
struct CustomerCardPoints: View {
var body: some View {
// Responsiv on iPhone + Pro
// iPhone Max is not implemented
VStack(spacing: 0){
ZStack() {
Image("Slice")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 180, maxHeight: 60)
.offset(x: 160, y: -127)
HStack(alignment: .firstTextBaseline) {
Text("22")
.font(.system(size: 18))
.fontWeight(.bold)
.foregroundColor(.white)
.offset(x: 233, y: -126)
Image("favoriten")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 110, height: 25)
.contentShape(Rectangle())
.offset(x: 132, y: -120)
}
}
.onTapGesture {
print("Hier kommt die Navigation zur Customer Card")
}
}
}
}
I tried to unwrap / wrap the Struct in the view so the alignment would be different, but I rather destroyed the code as being closer to the result.
I have circle image, and I want to put pencil image button like below in image, I try to use
.padding(.vertical, 40) .padding(.horizontal,20)
or
.padding(.top,40)
.padding(.leading,20)
but it is not work. Any idea?
ZStack{
Image("image")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 120, height: 120)
.clipShape(Circle())
Button() {
} label: {
Image("pencil")
}
}
You could achieve the layout by calculating the right offset for the camera Button as you want the button to be placed on the edge of the circle:
let radius: CGFloat = 100
var offset: CGFloat {
sqrt(radius * radius / 2)
}
var body: some View {
Image(imageName)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(
width: radius * 2,
height: radius * 2)
.clipShape(Circle())
.overlay(
Button(action: {}) {
Image(systemName: "camera.fill")
.foregroundColor(.primary)
.padding(8)
.background(Color.gray)
.clipShape(Circle())
.background(
Circle()
.stroke(Color.white, lineWidth: 2)
)
}.offset(x: offset, y: offset)
)
}
gives a result like this:
You could obviously either hardcode the radius of the circle or pass it into the initialiser of this View if needed.
Set alignment of ZStack
ZStack(alignment: .bottomTrailing){ //<-- Here
Image("image")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 120, height: 120)
.clipShape(Circle())
Button() {
} label: {
Image("pencil")
}
}
I need to present accurate star rating like 3.1, 4.8 using SwiftUI.
The desired result should be like this:
Your general approach is good, but I believe it can be made much simpler.
The below code adapts to whatever size it is placed in (so if you want a specific size, put it in a frame).
Note that the internal ZStack isn't required in iOS 14, but GeometryReader still doesn't document its layout behavior (except in an Xcode 12 release note), so this makes it explicit.
struct StarsView: View {
var rating: CGFloat
var maxRating: Int
var body: some View {
let stars = HStack(spacing: 0) {
ForEach(0..<maxRating, id: \.self) { _ in
Image(systemName: "star.fill")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
stars.overlay(
GeometryReader { g in
let width = rating / CGFloat(maxRating) * g.size.width
ZStack(alignment: .leading) {
Rectangle()
.frame(width: width)
.foregroundColor(.yellow)
}
}
.mask(stars)
)
.foregroundColor(.gray)
}
}
This draws all the stars in gray, and then creates a yellow rectangle of the correct width, masks it to the stars, and draws that on top as an overlay. Overlays are automatically the same size as the view they're attached to, so you don't need all the frames to make the sizes match they way you do with a ZStack.
After spending some time I did found a solution.
struct StarsView: View {
let rating: CGFloat
let maxRating: CGFloat
private let size: CGFloat = 12
var body: some View {
let text = HStack(spacing: 0) {
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
Image(systemName: "star.fill")
.resizable()
.frame(width: size, height: size, alignment: .center)
}
ZStack {
text
HStack(content: {
GeometryReader(content: { geometry in
HStack(spacing: 0, content: {
let width1 = self.valueForWidth(geometry.size.width, value: rating)
let width2 = self.valueForWidth(geometry.size.width, value: (maxRating - rating))
Rectangle()
.frame(width: width1, height: geometry.size.height, alignment: .center)
.foregroundColor(.yellow)
Rectangle()
.frame(width: width2, height: geometry.size.height, alignment: .center)
.foregroundColor(.gray)
})
})
.frame(width: size * maxRating, height: size, alignment: .trailing)
})
.mask(
text
)
}
.frame(width: size * maxRating, height: size, alignment: .leading)
}
func valueForWidth(_ width: CGFloat, value: CGFloat) -> CGFloat {
value * width / maxRating
}
}
Usage:
StarsView(rating: 2.4, maxRating: 5)
Another simple solution that enables star fill level through clipping. You can use it with following API:
/// Pass in rating as a Binding or provide a constant
StarRating(rating: .constant(1.6), maxRating: 5)
.font(.title2) // Provide font for sizing
Which looks like:
Implementation:
struct StarRating: View {
struct ClipShape: Shape {
let width: Double
func path(in rect: CGRect) -> Path {
Path(CGRect(x: rect.minX, y: rect.minY, width: width, height: rect.height))
}
}
#Binding var rating: Double
let maxRating: Int
init(rating: Binding<Double>, maxRating: Int) {
self.maxRating = maxRating
self._rating = rating
}
var body: some View {
HStack(spacing: 0) {
ForEach(0..<maxRating, id: \.self) { _ in
Text(Image(systemName: "star"))
.foregroundColor(.blue)
.aspectRatio(contentMode: .fill)
}
}.overlay(
GeometryReader { reader in
HStack(spacing: 0) {
ForEach(0..<maxRating, id: \.self) { _ in
Image(systemName: "star.fill")
.foregroundColor(.blue)
.aspectRatio(contentMode: .fit)
}
}
.clipShape(
ClipShape(width: (reader.size.width / CGFloat(maxRating)) * CGFloat(rating))
)
}
)
}
}
I am used to Interface Builder and layout constraints but now I want to convert my app to Swift UI. What I am trying to do right now is align the top edge of the view marked with a 1 to be within a certain distance of the safe area bottom edge (marked with a 2) so that the top edge that is now at 1 will then be at position 3. I tried using spacers but then it will look different on smaller devices such as an iPhone 8. In IB I could have used a simple layout constraint. How does this work in Swift UI? I have attached the relevant code and an image. Thank you for your help.
struct ContentView: View {
init() {
UINavigationBar.appearance().backgroundColor = .orange
}
var body: some View {
NavigationView {
VStack{
Spacer()
ZStack{
Rectangle()
.fill(Color(hue: 0, saturation: 0, brightness: 0, opacity: 0.1))
Image("")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 150)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)
}.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.width, alignment: .center)
Spacer(minLength: 100)
ZStack(alignment: .bottom){
ExtractedView()
.edgesIgnoringSafeArea(.bottom)
}
}
.navigationBarTitle(Text("You See"))
.navigationBarHidden(false)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.previewDevice("iPhone")
}
}
struct ExtractedView: View {
#State private var name: String = ""
var body: some View {
VStack{
ZStack(alignment: .top){
VStack{
RoundedRectangle(cornerRadius: 50)
.frame(width: 60, height: 7)
.padding(.top)
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Start advertising")
.font(.headline)
.foregroundColor(Color.black)
}.padding(.top)
TextField("Test", text: $name)
.padding(.all)
.background(Color.white.cornerRadius(20))
.padding()
}
RoundedRectangle(cornerRadius: 30)
.fill(Color(hue: 0, saturation: 0, brightness: 0, opacity: 0.1))
.zIndex(-5)
}
}
}
}
Ok, so I was able to solve my problem. The trick is to create two VStacks with different frame alignments. The outer VStack has top alignment so that the Discover view can be at the top. The inner VStack has a bottom alignment so that the sheet can be pulled up from the bottom. Space will be filled from the bottom up in this case.
VStack{
Discover()
.padding(.top, 60.0)
VStack{
Text("Recent Messages:")
.font(.headline)
DetailSheet()
.offset(x: 0, y: 0)
}.frame(width: g.size.width, height: (g.size.height - g.size.width - 80 + 200), alignment: .bottom)
}
.frame(width: g.size.width, height: g.size.height, alignment: .top)
you can try this:
offset -10 is just your offset you want to have....
i hope i understood you right, i am not so sure...
var body: some View {
VStack{
// ZStack(alignment: .top){
VStack{
RoundedRectangle(cornerRadius: 50)
.frame(width: 60, height: 7)
.padding(.top)
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/) {
Text("Start advertising")
.font(.headline)
.foregroundColor(Color.black)
}.padding(.top)
TextField("Test", text: $name)
.padding(.all)
.background(Color.white.cornerRadius(20))
.padding()
}.background(RoundedRectangle(cornerRadius: 30)
.fill(Color(hue: 0, saturation: 0, brightness: 0, opacity: 0.1))
).offset(y:-10)
}
// }