distance between ui components in swiftUi - swiftui

I want to have a constant distance between the image and the text , I also want to have a constant distance between the image and trailing anchor of the green box.using padding and increase the length of the text the problem appears as you seen win the picture. how can I solve this problem? how can use auto layout in swiftUi the same way as UIKit?
import SwiftUI
struct SwiftUIView: View {
var orgName:String
var imgName:String
var body: some View {
ZStack{
RoundedRectangle(cornerRadius: 8)
.fill(Color.green)
.frame( height: 50)
.padding([.trailing, .leading], 20)
.padding([.top, .bottom], 10)
.overlay(
HStack(spacing: 10){
Text("\(orgName)")
.frame(width: .infinity, height: 50)
//Image( "\(imgName)")
Image( systemName: "phone.fill")}
//.padding(.trailing
// ,UIScreen.main.bounds.size.width-480)
)
}
}
}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIView(orgName:"aaaaa", imgName:"aimg")
}
}

In SwiftUI the order of modifiers matters! You need to set the overlay before the paddings.

You can use the below code for Overlay HStackView padding -
struct SwiftUIView: View {
var orgName:String
var imgName:String
var body: some View {
ZStack{
RoundedRectangle(cornerRadius: 8)
.fill(Color.green)
.frame( height: 50)
.overlay(
HStack {
Text("\(orgName)")
.frame(width: .infinity, height: 50)
Image(systemName: "phone.fill")
}.padding(),
alignment: .trailing
)
.padding([.top, .bottom], 10)
.padding([.trailing, .leading], 20)
}
}
}
Please check the result for your updated code here -

The important thing is swiftUI modifiers should use in correct order
So You can simply use paddings instead of frames. Then this will responsive for all devices as well.
import SwiftUI
struct SwiftUIView: View {
//MARK: - PRROPERTIES
var orgName:String
//MARK: - BODY
var body: some View {
HStack(spacing: 10){
Spacer()
Text("\(orgName)")
Image( systemName: "phone.fill")
}
.padding(.vertical , 15)
.padding(.horizontal, 10)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.green)
)
.padding(.horizontal, 15)
}
}
//MARK: - PREVIEW
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
SwiftUIView(orgName:"aaaaaa")
}
}
preview

Related

Scrollable content not working as expected in SwiftUI

I'm trying to create a scroll view with my custom view, but when I add scroll to view it's not working as expected, without scroll view working fine.
struct ContentView: View {
var body: some View {
ScrollView(.vertical) {
VStack {
ForEach (0..<2) { _ in
ListItem()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// But the below code is working fine.
struct ContentView: View {
var body: some View {
VStack {
ForEach (0..<2) { _ in
ListItem()
}
}
}
}
// List Item
struct ListItem: View {
var body: some View {
VStack {
HStack {
Image("steve")
.resizable()
.clipShape(Circle())
.aspectRatio(contentMode: .fit)
.frame(maxWidth:44, maxHeight: 44)
VStack {
Text("Steve Jobs")
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
Text("1 hour ago")
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
}
Spacer()
}
ZStack(alignment:.top) {
GeometryReader { geometry in
VStack {
ZStack {
Image("poster_1")
.resizable()
.aspectRatio(contentMode: .fit)
.cornerRadius(8)
.shadow(color: Color.black.opacity(0.12),
radius: 4, x: 1, y: 1)
.frame(width: geometry.size.width - 64,
height: geometry.size.height * 0.35)
.padding([.horizontal], 32)
.clipped()
ZStack {
Rectangle()
.fill(Color.black.opacity(0.75))
.frame(maxWidth:84 , maxHeight: 84)
.cornerRadius(12)
Image(systemName: "play.fill")
.font(.system(size: 44, weight: .bold))
.foregroundColor(.white)
}
}
VStack {
Text("Game of Thrones")
.accentColor(Color.gray.opacity(0.25))
.font(Font.subheadline.weight(.bold))
.padding([.horizontal], 32)
.padding([.bottom], 2)
.frame(maxWidth: .infinity,
alignment: .leading)
VStack {
Text("Game of Thrones is an American fantasy drama television series created by David Benioff and D. B. Weiss for HBO. ")
.accentColor(Color.gray.opacity(0.25))
.font(.footnote)
.frame(maxWidth: .infinity,
alignment: .leading)
.padding([.horizontal], 32)
Text("Show more...")
.accentColor(Color.gray.opacity(0.01))
.font(Font.footnote.weight(.bold))
.frame(maxWidth: .infinity,
alignment: .trailing)
.padding([.trailing], 32).onTapGesture {
print("okay")
}
}
}
}
}
}
}
}
}
ListItem contains multiple views which creates publisher info and movie information as shown below image.
Scrollview is scrolling but images are not shown in view as first image.
It is the geometry reader that you have in ListItem. Because neither a GeometryReader nor a Scrollview have their own size. Since neither no what size to render, they collapse. This is what you are seeing in your view. See this answer. The solution is to put the GeometryReader into ContentView outside the Scrollview and send the GeometryProxy that you called geometry into ListItem something like this:
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
ScrollView(.vertical) {
VStack {
ForEach (0..<2) { _ in
ListItem(geometry: geometry)
}
}
} // Scrollview
} // GeometryReader
}
}
struct ListItem: View {
let geometry: GeometryProxy
var body: some View {
...
}
This seems to fix it in Preview, though you may have to change your multipliers in the .frame() that uses geometry to size it how you want.

SwiftUI How to vertically center a view inside a VStack

I would like to have one view in the vertical center of the screen, one view at the top of the screen and one view vertically centered between these two views like so:
This took me 5min to do on a storyboard but I don't seem to find a way to do it in SwiftUI 🙃.
I already tried with multiple zstacks, vstacks, multiple custom alignments but this is the closest I got:
struct SelectionView: View {
var body: some View {
ZStack(alignment: .myAlignment) {
Color.green
.edgesIgnoringSafeArea(.all)
VStack {
Image(systemName: "clock")
.resizable()
.foregroundColor(.white)
.aspectRatio(contentMode: .fit)
.frame(width: 156, height: 80)
// Spacer()
Text("My\nmultiline label")
.multilineTextAlignment(.center)
.font(.title)
.foregroundColor(.white)
// Spacer()
VStack(spacing: 16) {
RoundedRectangle(cornerRadius: 5).fill(Color.white).frame(height: 79)
RoundedRectangle(cornerRadius: 5).fill(Color.white).frame(height: 79)
}
.alignmentGuide(VerticalAlignment.myAlignment) { dimension in
dimension[VerticalAlignment.center]
}
.layoutPriority(1)
}
.padding([.leading, .trailing], 24)
}
}
}
struct SelectionView_Previews: PreviewProvider {
static var previews: some View {
LanguageSelectionView()
}
}
// MARK
extension HorizontalAlignment {
enum MyHorizontal: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat
{ d[HorizontalAlignment.center] }
}
static let myAlignment =
HorizontalAlignment(MyHorizontal.self)
}
extension VerticalAlignment {
enum MyVertical: AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat
{ d[VerticalAlignment.center] }
}
static let myAlignment = VerticalAlignment(MyVertical.self)
}
extension Alignment {
static let myAlignment = Alignment(horizontal: .myAlignment,
vertical: .myAlignment)
}
I'm keeping the GeometryReader as a last resort as it feels like a too drastic measure for this seemingly simple layout..
I guess I'm approaching this in some wrong way (still too much UIKit/Constraints in my head)..
Here is possible solution. Tested with Xcode 12 / iOS 14
struct SelectionView: View {
var body: some View {
ZStack {
Color.green
.edgesIgnoringSafeArea(.all)
VStack(spacing: 16) {
Color.clear
.overlay(
VStack {
Image(systemName: "clock")
.resizable()
.foregroundColor(.white)
.aspectRatio(contentMode: .fit)
.frame(width: 156, height: 80)
Color.clear
.overlay(
Text("My\nmultiline label")
.multilineTextAlignment(.center)
.font(.title)
.foregroundColor(.white)
)
}
)
RoundedRectangle(cornerRadius: 5).fill(Color.white).frame(height: 79)
RoundedRectangle(cornerRadius: 5).fill(Color.white).frame(height: 79)
Color.clear
}
.padding([.leading, .trailing], 24)
}
}
}

iOS 14 Medium Widget Size Background Image Refuses to Fill

For some reason I'm unable to get an image background to aspectFill correctly in XCode Version 12.0, but only on the .systemMedium widget size. It seems to work perfectly on the small and large sizes.
I've got a pretty simple View:
import SwiftUI
#available(iOS 13.0.0, *)
struct Banana: View {
var body: some View {
VStack(alignment: .leading){
Spacer()
Text("Aardvark Exactlywhat")
.font(.largeTitle)
.bold()
.padding(.bottom, 20)
.padding(.leading, 20)
.padding(.trailing, 20)
.minimumScaleFactor(0.5)
.foregroundColor(.white)
.shadow(
color: Color.black,
radius: 1.0,
x: CGFloat(4),
y: CGFloat(4))
}
.edgesIgnoringSafeArea(.all)
.background(
Image("bananas")
.resizable()
.scaledToFill()
).edgesIgnoringSafeArea(.all)
}
}
#available(iOS 13.0.0, *)
struct Banana_Previews: PreviewProvider {
static var previews: some View {
Banana()
}
}
And a pretty simple widget:
struct fruitWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
Banana()
}
}
#main
struct fruitWidget: Widget {
let kind: String = "fruitWidget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
fruitWidgetEntryView(entry: entry)
}
.configurationDisplayName("fruit Widget")
.description("Enhance your day with delicious fruit.")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
}
}
struct fruitWidget_Previews: PreviewProvider {
static var previews: some View {
Group{
fruitWidgetEntryView(entry: SimpleEntry(date: Date()))
.previewContext(
WidgetPreviewContext(family: .systemSmall))
fruitWidgetEntryView(entry: SimpleEntry(date: Date()))
.previewContext(
WidgetPreviewContext(family: .systemMedium))
fruitWidgetEntryView(entry: SimpleEntry(date: Date()))
.previewContext(
WidgetPreviewContext(family: .systemLarge))
}
}
}
I've tried changing the aspect ratio, using GeometryReader and frame(), and a dozen other variations. Regardless of what I try, I get white space on the left and right in the medium widget. It only works for the large and small size. See image:
Here is fixed variant - as image in the background you have to expand VStack to full screen.
Note: edgesIgnoringSafeArea allows to go beyond safe area when content is wider, but not make content wide.
var body: some View {
VStack(alignment: .leading){
Spacer()
Text("Aardvark Exactlywhat")
.font(.largeTitle)
.bold()
.padding(.bottom, 20)
.padding(.leading, 20)
.padding(.trailing, 20)
.minimumScaleFactor(0.5)
.foregroundColor(.white)
.shadow(
color: Color.black,
radius: 1.0,
x: CGFloat(4),
y: CGFloat(4))
}
.frame(maxWidth: .infinity, maxHeight: .infinity) // << this one !!
.edgesIgnoringSafeArea(.all)
.background(
Image("plant")
.resizable()
.scaledToFill()
)
}

SwiftUI GeometryReader compact size

I would like my LoadingTitle to have a width of 70% of the screen so I use the GeometryReader but it makes the vertical size expand and my LoadingTitle takes much more vertical space. I would like it to remain as compact as possible.
When using hardcoded width: 300 I get the correct layout (except the relative width):
struct ContentView: View {
var body: some View {
VStack(alignment: .leading, spacing: 0) {
LoadingTitle()
Color.blue
}
}
}
struct LoadingTitle: View {
var body: some View {
HStack() {
Color.gray
}
.frame(width: 300, height: 22)
.padding(.vertical, 20)
.border(Color.gray, width: 1)
}
}
Now if I wrap the body of my LoadingTitle in the GeometryReader I can get the correct relative size but then the GeometryReader expands my view vertically:
struct LoadingTitle: View {
var body: some View {
GeometryReader { geo in
HStack() {
Color.gray
.frame(width: geo.size.width * 0.7, height: 22, alignment: .leading)
Spacer()
}
.padding(.vertical, 20)
.border(Color.gray, width: 1)
}
}
}
I tried using .fixedSize(horizontal: false, vertical: true) on the GeometryReader as other suggested but then the resulting view is too much compact and all its paddings are ignored:
How could I achieve the layout of the 1st screenshot with a relative width?
Here is possible approach. Tested with Xcode 11.4 / iOS 13.4 (w/ ContentView unchanged)
struct LoadingTitle: View {
var body: some View {
VStack { Color.clear }
.frame(height: 22).frame(maxWidth: .infinity)
.padding(.vertical, 20)
.overlay(
GeometryReader { geo in
HStack {
HStack {
Color.gray
.frame(width: geo.size.width * 0.7, height: 22)
}
.padding(.vertical, 20)
.border(Color.gray, width: 1)
Spacer()
}
}
)
}
}
Since you have a fixed height for title,
struct LoadingTitle1: View {
var body: some View {
GeometryReader { geo in
HStack {
Color.gray.frame(width: geo.size.width * 0.7)
.padding(.vertical, 20)
.border(Color.gray, width: 1)
Spacer()
}
}.frame(height: 62)
}
}

SwiftUI Text hight expend top and down. How do I expend only bottom

`import SwiftUI
struct Test2: View {
var body: some View {
ZStack {
Rectangle()
.fill(Color.green)
.frame(height:200)
.offset(y: 100)
VStack{
Image("star2")
.resizable()
.frame(width: 50, height: 50)
Text("So even if the text is suepr long, text height expend to bottom. not top and bottom")
.font(.system(size: 30))
.frame(width:300)
.padding()
}
}
}
}`
[
When the text is too long, SwiftUI Text() height changes. But it increases toward top and bottom.
it pushes out my other views.
How can I change Text() to increase height only to down side??
fix the space above the star!
import SwiftUI
struct ContentView: View {
#State var flag = false
let text = ["Hello World", """
So even if the text is suepr long, text height expend to bottom. not top and bottom
"""]
var body: some View {
GeometryReader { proxy in
ZStack {
Rectangle()
.fill(Color.green)
.frame(height:200)
.offset(y: 100)
VStack{
Color.clear.frame(height: proxy.size.height / 3)
Image(systemName: "star")
.resizable()
.frame(width: 50, height: 50).onTapGesture {
self.flag.toggle()
}
Text(self.text[self.flag ? 0 : 1])
.font(.system(size: 30))
.padding()
Spacer()
//Color.yellow
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}