Align Top In SwiftUI HStack - swift

I am trying to align an image in the top of an HStack, but when the text is multiple lines the image gets centered. How can I align the second image with the top of the text?
var infoView: some View {
VStack(alignment: .leading, spacing: 16) {
HStack(alignment: .top) {
Image("rocket")
.frame(alignment: .top)
Text("pqosejfr")
.font(.body).fontWeight(.bold)
}
HStack {
Image("bank")
Text("qwoijqoiwjoijweijqwoiejqoiwjefoiqwjefoiqjwefijqwoiejfqoiwjefoiqwjefpoiqjweoifjqpwoiejfpqoiwejfoiqwejfoiqjwefpoiqjwepoifjqpwoiejfqpoiwejfpoqiwejfpoiqwjefpoiqwjefpoiqjweofijqwepoifjpqwoiejfoij")
.font(.body).fontWeight(.bold)
}
}
}

I was simply missing"
HStack(alignment: .top)
On the second HStack. Boneheaded mistake.

You can use a TextField with the multilineTextAlignment set to .leading.
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(alignment: .leading, spacing: 16) {
HStack(alignment: .top) {
Image("rocket")
.frame(alignment: .top)
TextField("pqosejfr", text: /* #START_MENU_TOKEN# *//* #PLACEHOLDER=Value# *//* #END_MENU_TOKEN# */)
.font(.body).fontWeight(.bold)
}
HStack {
Image("bank")
TextField("qwoijqoiwjoijweijqwoiejqoiwjefoiqwjefoiqjwefijqwoiejfqoiwjefoiqwjefpoiqjweoifjqpwoiejfpqoiwejfoiqwejfoiqjwefpoiqjwepoifjqpwoiejfqpoiwejfpoqiwejfpoiqwjefpoiqwjefpoiqjweofijqwepoifjpqwoiejfoij", text: /* #START_MENU_TOKEN# *//* #PLACEHOLDER=Value# *//* #END_MENU_TOKEN# */)
.font(.body).fontWeight(.bold)
.multilineTextAlignment(.leading)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Related

How to make a List background black in SwiftUI?

What I did to make the entire screen black is to use this construction in my view:
ZStack {
Color.black.ignoresSafeArea() }
And for the List modifiers I used the following two:
.listRowBackground(Color.black)
.listStyle(.plain)
This works great.
But then I want to add a view on top of my List (like a header) and some white space appears around it ? Where does that come from ? And how can I change it to black ?
Here is my complete code:
struct WorkoutView: View {
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
List {
TempView()
ForEach(1..<30) { index in
VStack(alignment: .leading, spacing: 4) {
Text("one line")
.foregroundColor(.white)
.font(.title)
.fontWeight(.bold)
}
.padding(.vertical)
}
.listRowBackground(Color.black)
}
.listStyle(.plain)
}
}
}
struct TempView: View {
var body: some View {
ZStack(alignment: .leading) {
Color.black.ignoresSafeArea()
Text("Hello World")
.foregroundColor(.red)
.font(.largeTitle)
.bold()
}
}
}
Just do the same thing you did for the ForEach: add a .listBackground() modifier. Like this:
TempView()
.listRowBackground(Color.black)

How to call a view with a specific size?

I have a question about views in swiftui.
I have created a file CircleView which shows me a circle and a text in the middle. Additionally I have created a list in ContentView and a DetailView for details.
I now use the created circle in the list and on the detail view. Unfortunately the size of the circle is defined in the CircleView and I don't know how to change it individually so that it is displayed small in the list and uses the full width on the detail view.
So how can I change the size of a view individually?
Here is my code
CircleView:
struct CircleView: View {
var body: some View {
ZStack{
Circle()
.fill(Color.blue)
.frame(width: 250, height: 250)
Text("VIEW")
.frame(width: 100, height: 100)
.font(.largeTitle)
.foregroundColor(.white)
}
}
}
ContentView:
struct ContentView: View {
var body: some View {
NavigationView {
List{
NavigationLink(destination: DetailsView()) {
HStack{
Text("Hello")
Spacer()
CircleView()
}
}.navigationTitle("Home")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
DetailsView:
struct DetailsView: View {
var body: some View {
VStack{
Text("Hello, World!")
.padding(10)
CircleView()
}
}
}
You should remove the set frame from your view and apply it when using your view.
Your code edited:
struct Sample: View {
var body: some View {
NavigationView {
List{
NavigationLink(destination: DetailsView()) {
HStack{
Text("Hello")
Spacer()
CircleView()
.frame(width: 100 , height: 100) // <-- Update frame here
}
}.navigationTitle("Home")
}
}
}
}
struct CircleView: View {
var body: some View {
ZStack{
Circle()
.fill(Color.blue)
Text("VIEW")
.font(.largeTitle)
.foregroundColor(.white)
}
}
}
struct Sample_Previews: PreviewProvider {
static var previews: some View {
Sample()
}
}
struct DetailsView: View {
var body: some View {
VStack{
Text("Hello, World!")
.padding(10)
CircleView().padding()
}
}
}

Multiline text truncated in ScrollView

I'm having a rather peculiar issue with multiline texts inside a ScrollView. I have a LazyVStack in the ScrollView and have SomeRow (see below) Views inside this stack.
I have a long multiline description inside this SomeRow View that SwiftUI always cuts off.
I have tried all of the solutions here but they either broke the layout, didn't work or straight up caused the app to crash.
How it looks at the moment:
I've tried to reduce the reproducing code down to a minimum, but it is unfortunately still quite long, because I want to preserve what look I am trying to accomplish.
Here is the SomeListView:
import SwiftUI
struct SomeListView: View {
var body: some View {
VStack{
Text("Title")
.font(.title)
ScrollView{
LazyVStack{
ForEach(0..<4){_ in
SomeRow(entries: EntryContainer(entries:
[
Entry("DESC\nLONG DESCRIPTION WITH OVERFLOW"),
Entry("")
]
))
Spacer().frame(height: 5)
Divider()
}
}
}.padding(16)
}
}
}
struct SomeListView_Previews: PreviewProvider {
static var previews: some View {
SomeListView()
}
}
SomeRow View
import SwiftUI
struct SomeRow: View {
var entries: [Entry]
var body: some View {
VStack(alignment: .leading, spacing: 0){
Text("title").frame(maxWidth: .infinity, alignment: .leading)
Spacer().frame(height: 5)
ForEach(entries){entry in
HStack{
VStack{
Text("00:00 - 00:00")
Spacer()
}.frame(minWidth: 110)
Divider()
VStack{
HStack{
Text("titleinner")
Spacer()
}
HStack{
Text(entry.description ?? "")
Spacer()
VStack{
Spacer()
Text("number")
}
}
Spacer()
}
Spacer()
}
}
}
}
}
struct SomeRow_Previews: PreviewProvider {
static var previews: some View {
SomeRow(entries:
[
Entry("DESC\nLONG DESCRIPTION WITH OVERFLOW"),
Entry("")
]
)
}
}
Entry Data Class
import Foundation
class Entry: Identifiable {
let description: String?
init(_ description: String? = nil) {
self.description = description
}
}
Any ideas for a workaround? I am assuming this is simply SwiftUI bug because if you set the same long description for both entries it magically shows both descriptions fully, which really doesn't make sense to me.
This is how it breaks when I use Text(entry.description ?? "").fixedSize(horizontal: false, vertical: true:
(The divider doesn't fill the full height anymore and alignment of the timestamps in the left column is wrong)
It’s nearly impossible to have single divider and maintain the same alignment as you are looking for. The closest I could get you is shown in below code. Hope it satisfies requirement to some extent.
import SwiftUI
struct SomeListView: View {
var obj = [
Entry("DESC LONG DESCRIPTION WITH OVERFLOW"),
Entry("")
]
var body: some View {
VStack{
Text("Title")
.font(.title)
ScrollView{
LazyVStack(){
ForEach(0..<4){_ in
SomeRow(entries: obj)
Spacer().frame(height: 5)
Divider()
}
}
}.padding(16)
}
}
}
class Entry: Identifiable {
let description: String?
init(_ description: String? = nil) {
self.description = description
}
}
struct SomeRow: View {
var entries: [Entry]
var body: some View {
VStack(alignment: .leading,spacing:7){
Text("title")
ForEach(entries){entry in
HStack(alignment: .top,spacing:15){
Text("00:00 - 00:00")
Divider()
VStack(alignment:.leading, spacing:8){
Text("titleinner")
HStack(alignment:.lastTextBaseline, spacing:0){
Text(entry.description ?? "")
Spacer()
Text("number")
}
}
}.fixedSize(horizontal: false, vertical: true)
}
}
}
}
struct SomeRow_Previews: PreviewProvider {
static var previews: some View {
SomeRow(entries:
[
Entry("DESC\nLONG DESCRIPTION WITH OVERFLOW"),
Entry("")
]
)
}
}
struct SomeListView_Previews: PreviewProvider {
static var previews: some View {
SomeListView()
}
}
Output-:
With the help of concepts from the answer from Tushar Sharma I was able to create a version that displays the multiline text and has a continuous divider between the two "columns".
The only changes are in SomeRow View (compared to my initial question):
struct SomeRow: View {
var entries: [Entry]
var body: some View {
VStack(alignment: .leading, spacing: 0){
Text("title").frame(maxWidth: .infinity, alignment: .leading)
Spacer().frame(height: 5)
ForEach(entries){entry in
HStack(alignment: .top){
VStack{
Text("00:00 - 00:00")
Spacer()
}.frame(minWidth: 110)
Divider()
VStack(alignment: .leading){
Text("titleinner")
HStack(alignment: .lastTextBaseline){
Text(entry.description ?? "")
Spacer()
Text("number")
}
Spacer()
}
Spacer()
}.fixedSize(horizontal: false, vertical: true)
}
}
}
}
Basically using specified alignments and using fixedSize on the whole container of the text works wonders.
This is how it looks like now:

iOS 14 SwiftUI Keyboard lifts view automatically

I am using TextField in my view and when it becomes the first responder, it lefts the view as shown in the below GIF.
Is there any way I can get rid of this behavior?
Here is my code
NavigationView(content: {
ZStack{
MyTabView(selectedIndex: self.$index)
.view(item: self.item1) {
NewView(title: "Hello1").navigationBarTitle("")
.navigationBarHidden(true)
}
.view(item: self.item2) {
NewView(title: "Hello2").navigationBarTitle("")
.navigationBarHidden(true)
}
.view(item: self.item3) {
NewView(title: "Hello3").navigationBarTitle("")
.navigationBarHidden(true)
}
}.navigationBarHidden(true)
.navigationBarTitle("")
}).ignoresSafeArea(.keyboard, edges: .bottom)
// New View
struct NewView:View {
#State var text:String = ""
var title:String
var body: some View {
VStack {
Spacer()
Text("Hello")
TextField(title, text: self.$text)
.textFieldStyle(RoundedBorderTextFieldStyle())
}.padding()
.onAppear {
debugPrint("OnApper \(self.title)")
}
}
}
For .ignoresSafeArea to work you need to fill all the available area (eg. by using a Spacer).
The following will not work (no Spacers, just a TextField):
struct ContentView: View {
#State var text: String = ""
var body: some View {
VStack {
TextField("asd", text: self.$text)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
However, it will work when you add Spacers (fill all the available space):
struct ContentView: View {
#State var text: String = ""
var body: some View {
VStack {
Spacer()
TextField("asd", text: self.$text)
.textFieldStyle(RoundedBorderTextFieldStyle())
Spacer()
}
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
If you don't want to use Spacers you can also use a GeometryReader:
struct ContentView: View {
#State var text: String = ""
var body: some View {
GeometryReader { _ in
...
}
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
You should apply the modifier on the ZStack, NOT the NavigationView
NavigationView(content: {
ZStack{
,,,
}.navigationBarHidden(true)
.navigationBarTitle("")
.ignoresSafeArea(.keyboard, edges: .bottom) // <- This line moved up
})
Full working example:
struct ContentView: View {
#State var text = ""
var body: some View {
VStack{
Spacer()
Text("Hello, World")
TextField("Tap to test keyboard ignoring", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
.padding()
.ignoresSafeArea(.keyboard, edges: .bottom)
}
}
What eventually worked for me, combining answers posted here and considering also this question, is the following (Xcode 12.4, iOS 14.4):
GeometryReader { _ in
VStack {
Spacer()
TextField("Type something...", text: $value)
Spacer()
}.ignoresSafeArea(.keyboard, edges: .bottom)
}
Both spacers are there to center vertically the textfield.
Using only the GeometryReader or the ignoresSafeArea modifier didn't do the trick, but after putting them all together as shown above stopped eventually the view from moving up upon keyboard appearance.
That's what I figured out:
GeometryReader { _ in
ZStack {
//PUT CONTENT HERE
}.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
}
It seems to work for me. In this case you do not need to check iOS 14 availability.

Views compressed by other views in SwiftUI VStack and List

In my SwiftUI application, I'm trying to implement a UI similar to this:
I've added the two rows for category 1 and category 2. The result looks like this:
NavigationView {
VStack(alignment: .leading) {
CategoryRow(...)
CategoryRow(...)
Spacer()
}
.navigationBarTitle(Text("Featured"))
}
Now, when added the view for the third category – an VStack with images – the following happens:
This happened, after I replaced Spacer(), with said VStack:
VStack(alignment: .leading) {
Text("Rivers")
.font(.headline)
ForEach(self.categories["Rivers"]!.identified(by: \.self)) { landmark in
landmark.image(forSize: 200)
}
}
My CategoryRow is implemented as follows:
VStack(alignment: .leading) {
Text(title)
.font(.headline)
ScrollView {
HStack {
ForEach(landmarks) { landmark in
CategoryItem(landmark: landmark, isRounded: self.isRounded)
}
}
}
}
Question
It seems that the views are compressed. I was not able to find any compression resistance or content hugging priority modifiers to fix this.
I also tried to use .fixedSize() and .frame(width:height:) on CategoryRow.
How can I prevent the compression of these views?
Update
I've tried embedding the whole outer stack view in a scroll view:
NavigationView {
ScrollView { // also tried List
VStack(alignment: .leading) {
CategoryRow(...)
CategoryRow(...)
ForEach(...) { landmark in
landmark.image(forSize: 200)
}
}
.navigationBarTitle(Text("Featured"))
}
}
...and the result is worse:
You might prevent the views in VStack from being compressed by using
.fixedSize(horizontal: false, vertical: true)
For example:
I have the following VStack:
VStack(alignment: .leading){
ForEach(group.items) {
FeedCell(item: $0)
}
}
Which render compressed Text()
When I add .fixedSize(horizontal: false, vertical: true)
it doesn't compress anymore
VStack(alignment: .leading){
ForEach(group.items) {
FeedCell(item: $0)
.fixedSize(horizontal: false, vertical: true)
}
}
You could try to add a layoutPriority()operator to your first VStack. This is what the documentation says about the method:
In a group of sibling views, raising a view’s layout priority encourages that view to shrink later when the group is shrunk and stretch sooner when the group is stretched.
So it's a bit like the content compression resistance priority in Autolayout. But the default value here is 0, so you just have to set it to 1 to get the desired effect, like this:
VStack(alignment: .leading) {
CategoryRow(...)
CategoryRow(...)
Spacer()
}.layoutPriority(1)
VStack(alignment: .leading) {
...
}
Hope it works!
It looks like is not enough space for all your views in VStack, and it compresses some of them. You can embed it into the ScrollView
NavigationView {
ScrollView {
VStack(alignment: .leading) {
CategoryRow(...)
CategoryRow(...)
/// you images and so on
}
}
}
struct ContentView1: View {
var body: some View {
NavigationView {
ScrollView {
VStack {
CategoryListView {
CategoryView()
}
CategoryListView {
SquareCategoryView()
}
CategoryListView {
RectangleCategoryView()
}
}
.padding()
}
.navigationTitle("Featured")
}
}
}
struct CategoryListView<Content>: View where Content: View {
private let viewSize: CGFloat = 150
var content: () -> Content
init(#ViewBuilder content: #escaping () -> Content) {
self.content = content
}
var body: some View {
VStack {
HStack {
Text("Category name")
Spacer()
}
ScrollView(.horizontal, showsIndicators: false){
HStack {
ForEach(0..<10) { _ in
content()
}
}
}
}
}
}
struct ContentView1_Previews: PreviewProvider {
static var previews: some View {
ContentView1()
}
}
struct CategoryView: View {
private let viewSize: CGFloat = 150
var body: some View {
Circle()
.fill()
.foregroundColor(.blue)
.frame(width: viewSize, height: viewSize)
}
}
struct RectangleCategoryView: View {
private let viewSize: CGFloat = 350
var body: some View {
Rectangle()
.fill()
.foregroundColor(.blue)
.frame(width: viewSize, height: viewSize * 9 / 16)
}
}
struct SquareCategoryView: View {
private let viewSize: CGFloat = 150
var body: some View {
Rectangle()
.fill()
.foregroundColor(.blue)
.frame(width: viewSize, height: viewSize)
}
}
I think your topmost view (in the NavigationView) needs to be a List, so that it is scrollable:
NavigationView {
List {
...
Or use a ScrollView.
A stack automatically fits within a screen. If you want your content to exceed this, you would have used a ScrollView or a TableView etc i UIKit
EDIT:
Actually, a little Googling brought this result, which seems to be exactly what you are making:
https://developer.apple.com/tutorials/swiftui/composing-complex-interfaces