SwiftUI : How to Fix leading and trailing with margin 10 - ios

This is my first trial on SwiftUI, where I am trying to create a UITable view like UI.
I am trying to give fix leading and trailing (not fix width) to cell/views, I have given ample of time and now this is what I have tried with output:
Git : Here is the link to source code to reproduce this issue

You need to use .padding modifier for margin. In your code, you have to add padding inside your ScrollView.
{
VStack(alignment: .center){
ForEach(boxes) { box in
BoxViewTable(box: box)
.background(Color.white).padding(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
}
After that, inside BoxViewTable, you need to add .frame modifier.
HStack{
Image("\(box.imgUrl)")
.resizable()
.frame(width: 80, height: 100, alignment: .leading)
VStack(alignment:.leading){
Text("\(box.newsTitle)")
.lineLimit(2)
Text("\(box.newsSubTitle) - \(box.dateTime)")
}
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading)
Finally, Don't give up :-)

Related

How to properly fit a filled Image in its bounds on SwiftUI?

I have to fix a problem with Images on SwiftUI. I currently have to fill the images I receive in a certain vertical container, which are the cells of a LazyVGrid, but the problem is that the tappable area expands over the designated one.
I used the Accessibility Inspector, and I found out that the area in excess is due the Images. (screenshot attached)
The code I'm using is something similar to the following:
Image(uiImage: image.image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(
maxWidth: width,
maxHeight: height
)
.clipped()
I thought that by having the frames and the clipped method I could get rid of this problem, but that's not the case unless I'm using some squared images. Any idea of what could work to fix this problem? Thanks!
Just add .contentShape(Rectangle()) at the bottom:
Image(uiImage: image.image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(
maxWidth: width,
maxHeight: height
)
.clipped()
.contentShape(Rectangle()) // <- Here!
Wrap content in Geometry Reader and you need to know the aspect ratio like I set 720/460 below
GeometryReader { gr in
Image(uiImage: image.image)
.resizable()
.aspectRatio(720/460, contentMode: .fit)
}
.clipped()
.aspectRatio(720/460, contentMode: .fit)

SwiftUI Move Text within HStack to next line

I have a scenario where some text within an HStack may need to be multiple lines. I would like for the text to extend into the next line so a user would see 2 lines both left-aligned.
Here is some base setup in my View:
VStack(alignment: .center) {
HStack(spacing: 4) {
HStack(spacing: 4) {
Text("display name")
Image("display image").resizable().frame(width: 10, height: 10, alignment: .center)
}
Text("blah blah blah") // This text can extend to more than 1 line...
.lineLimit(nil) // so the text will be more than 1 line...
.frame(maxWidth: .infinity, alignment: .leading)
.fixedSize(horizontal: false, vertical: true)
Spacer()
}
.padding(EdgeInsets(top: 32, leading: 0, bottom: 0, trailing: 16))
Spacer()
// more stuff here
}
So what happens here is that my text does extend to more than one line, but it stays within its own little box, it doesn't move into a whole new line so it'll be under the first text within this HStack.
Update:
I ran this code in a blank project. Here is a screenshot for a visual example. It is not displaying how I want it to.
The goal is to keep it aligned and then move the second line below to start where the display name starts.

XCode (Bug?) (SwiftUI): Rather than Scale and Change Opacity where they are, my Views are coming in from the edge of the screen

I am trying to have a circle on my screen with two circles behind it in a ZStack that ease in and out with the ScaleEffect(), and change opacity. I made this in a separate SwiftUI file, where there seemed to be no issues, but once I put it in my ContentView() it this weird bug seemed to occur.
Please ignore the circles in the background, that's just my background view. But see those two circles of slightly different shades of blue? They keep entering and exiting the screen, coming behind the dark blue "plus" icon and then leaving again. Meanwhile, I'd like them to simply be behind the "plus" circle.
enter image description here
enter image description here
enter image description here
Is this because of some kind of XCode bug? Or did I write something wrong in my code?
I would really appreciate it if somebody could clarify :)
Here is my code. I made an #State private var buttonIsAnimating and defaulted it to false, and said that once the button appears, the circles should start animating. Is something wrong with the code?
ZStack {
Group {
Circle()
.fill(Color("Background2").opacity(self.buttonIsAnimating ? 0.6 : 0))
.frame(width: 75, height: 75, alignment: .center)
.scaleEffect(self.buttonIsAnimating ? 1 : 0)
Circle()
.fill(Color("Background3").opacity(self.buttonIsAnimating ? 0.7 : 0))
.frame(width: 89, height: 89, alignment: .center)
.scaleEffect(self.buttonIsAnimating ? 1 : 0)
}
.animation(Animation.easeInOut(duration: 2).repeatForever(autoreverses: true))
Button(action: {
self.showingAddANewToDoView.toggle()
}) {
Image(systemName: "plus.circle.fill")
.resizable()
.scaledToFit()
.background(Circle().fill(Color("Background")))
.foregroundColor(Color("Background4"))
.frame(width: 60, height: 60)
.padding(5)
}//: Button
.onAppear {
self.buttonIsAnimating.toggle()
}
}
Try with link animation to state, like
Group {
Circle()
.fill(Color("Background2").opacity(self.buttonIsAnimating ? 0.6 : 0))
.frame(width: 75, height: 75, alignment: .center)
.scaleEffect(self.buttonIsAnimating ? 1 : 0)
Circle()
.fill(Color("Background3").opacity(self.buttonIsAnimating ? 0.7 : 0))
.frame(width: 89, height: 89, alignment: .center)
.scaleEffect(self.buttonIsAnimating ? 1 : 0)
}
.animation(Animation.easeInOut(duration: 2).repeatForever(autoreverses: true),
value: self.buttonIsAnimating)

Add horizontal padding to TextEditor but prevent it from shifting scrollbar inside as a result

I'm using a TextEditor that I'd like to include horizontal padding for, so that the text doesn't stick to either edge. However, the problem is if I include it, and the text within is scrollable, then the scrollbar is not positioned to the far-right, but has instead been moved inside by the padding amount.
TextEditor(text: $text)
.background(Color.white)
.foregroundColor(Color.black)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.customFont(.body)
.lineSpacing(8)
.padding(.leading)
.padding(.trailing)
You added padding to external frame, but need to indent internal text container. The possible solution (as TextEditor is actually UITextView) to use appearance. So the solution would be to add the following in parent view of TextEditor
init() {
UITextView.appearance().textContainerInset =
UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12) // << !!
}
// ... other code
TextEditor(text: $text)
.background(Color.white)
.foregroundColor(Color.black)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.customFont(.body)
.lineSpacing(8)
Tested with Xcode 12 / iOS 14

SwiftUI text-alignment

Among the many properties of the Text view, I couldn't find any related to text alignment. I've seen in a demo that it automatically handles RTL, and when placing stuff using View's body, it always centers it automatically.
Is there some concept that I'm missing about layout system in SwiftUI and if not, how can I set the text alignment properties to the Text?
You can do this via the modifier .multilineTextAlignment(.center).
Text("CENTER")
.multilineTextAlignment(.center)
Apple Documentation
From SwiftUI beta 3 forward, you can center a text view with the frame modifier:
Text("Centered")
.frame(maxWidth: .infinity, alignment: .center)
Was trying to understand this myself as other answers here mention Text.multilineTextAlignment(_:) / VStack(alignment:) / frame(width:alignment:) but each solution solves a specific problem. Eventually it depends on the UI requirement and a combination of these.
VStack(alignment:)
The alignment here is for the inner views in respective to one another.
So specifying .leading would associate all inner views to have their leading aligned with one another.
VStack(alignment: .leading, spacing: 6) {
Text("Lorem ipsum dolor")
.background(Color.gray.opacity(0.2))
Text("sit amet")
.background(Color.gray.opacity(0.2))
}
.background(Color.gray.opacity(0.1))
.frame
In frame(width:alignment:) or frame(maxWidth:alignment:), the alignment is for the contents within the given width.
VStack(alignment: .leading, spacing: 6) {
Text("Lorem ipsum dolor")
.background(Color.gray.opacity(0.2))
Text("sit amet")
.background(Color.gray.opacity(0.2))
}
.frame(width: 380, alignment: .trailing)
.background(Color.gray.opacity(0.1))
The inners views are leading aligned respective to one another but the views themselves are trailing aligned respective to the VStack.
.multilineTextAlignment
This specifies the alignment of the text inside and can be seen best when there are multiple lines otherwise without a defined frame(width:alignment), the width is automatically adjusted and gets affected by the default alignments.
VStack(alignment: .trailing, spacing: 6) {
Text("0. automatic frame\n+ view at parent's specified alignment\n+ multilineTA not set by default at leading")
.background(Color.gray.opacity(0.2))
Text("1. automatic frame\n+ view at parent's specified alignment\n+ multilineTA set to center")
.multilineTextAlignment(.center)
.background(Color.gray.opacity(0.2))
Text("2. automatic frame\n+ view at parent's specified alignment\n+ multilineTA set to trailing")
.multilineTextAlignment(.trailing)
.background(Color.gray.opacity(0.2))
}
.frame(width: 380, alignment: .trailing)
.background(Color.gray.opacity(0.1))
Tests with combinations:
VStack(alignment: .trailing, spacing: 6) {
Text("1. automatic frame, at parent's alignment")
.background(Color.gray.opacity(0.2))
Text("2. given full width & leading alignment\n+ multilineTA at default leading")
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.gray.opacity(0.2))
Text("3. given full width & center alignment\n+ multilineTA at default leading")
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.gray.opacity(0.2))
Text("4. given full width & center alignment\n+ multilineTA set to center")
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.gray.opacity(0.2))
Text("5. given full width & center alignment\n+ multilineTA set to trailing")
.multilineTextAlignment(.trailing)
.frame(maxWidth: .infinity, alignment: .center)
.background(Color.gray.opacity(0.2))
Text("6. given full width but no alignment\n+ multilineTA at default leading\n+ leading is based on content, looks odd sometimes as seen here")
.frame(maxWidth: .infinity)
.background(Color.gray.opacity(0.2))
}
.frame(width: 380)
.background(Color.gray.opacity(0.1))
I've actually run into the problem where I had to align text on a single line. What I've found to work is this:
Text("some text")
.frame(alignment: .leading)
If you combine this with the frame width parameter you can get some nice text block formatting for labels and such.
I guess SwiftUI wants us to use wrappers like stacks for such things.
So instead of writing something like Text("Hello World").aligned(.leading), the following is encouraged:
VStack(alignment: .leading) {
Text("Hello World")
}
We need to align the Text and not the Stack it's in. So calling multilineTextAlignment(.center) and setting the line limits I can be able to see the texts aligned to center. I don't know why I have to set the line limits, I thought it would expand if you have a large text.
Text("blahblah")
.font(.headline)
.multilineTextAlignment(.center)
.lineLimit(50)
If you would like to keep constant width for the Text, the ".multilineTextAlignment(.leading)" won't take any effect until there is only one line of text.
This is the solution that worked for me:
struct LeftAligned: ViewModifier {
func body(content: Content) -> some View {
HStack {
content
Spacer()
}
}
}
extension View {
func leftAligned() -> some View {
return self.modifier(LeftAligned())
}
}
Usage:
Text("Hello").leftAligned().frame(width: 300)
I had the same problem.
i used this for fixing that.
Text("Test")
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
You can set alignment for Vertical stackView as leading. Like below
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
Text("Joshua Tree National Park")
.font(.subheadline)
}
I'd like to use Spacer() view to aligning text block.
This example show text at the trailing side:
HStack{
Spacer()
Text("Wishlist")
}
You can always add a frame to the Text field and can modify it's alignment.
Text("Hello World!")
.frame(alignment : .topLeading)
Since, this is just for a couple of lines - this is better than using alignment on either of the Stacks
Not sure if this is the answer you are looking for but I have experienced that SwiftUI automatically switches to RTL for languages like Arabic, you don't need to explicitly specify that like in UIKit.
You can use this property of SwiftUI
multilineTextAlignment
for TextAlignment.
VStack {
Text("Your Text")
.multilineTextAlignment(.center)
}

Resources