Dynamic height for TextEditor - swiftui

I'm trying to fit the TextEditor inside a ScrollView.
Is there a way to make TextEditor only takes up the space that it needs to fit all text?
or simply, how to change the height of the TextEditor dynamically to fit all the text?

You can put it in a ZStack with an invisible Text for sizing:
ZStack {
TextEditor(text: $text)
Text(text).opacity(0).padding(.all, 8) // <- This will solve the issue if it is in the same ZStack
}

using
TextEditor(text: someTextBinding)
.fixedSize(horizontal: false, vertical: true)
does the job for me (== height is dynamic, width is static)

Dynamic height can also be achieved with combination use of Text and overlay() with TextEditor.
Text(textVal.isEmpty ? "Your placeholder" : textVal)
.font(.body)
.padding(.vertical, 8)
.padding(.horizontal, 18)
.foregroundColor(Color.gray)
.opacity(textVal.isEmpty ? 1 : 0)
.frame(maxWidth: .infinity, minHeight: 40, alignment: .leading)
.background(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray.opacity(0.2), lineWidth: 1)
.padding(.horizontal, 15)
)
.overlay(
TextEditor(text: $textVal)
.font(.body)
.foregroundColor(Color.black)
.multilineTextAlignment(.leading)
.padding(.horizontal, 15)
)
For transparent background of TextEditor add below code on View init:
init(yourProperty: String) {
self.yourProperty = yourProperty
UITextView.appearance().backgroundColor = .clear
}

Related

Custom navigation bar's button doesn't work (SwiftUI)

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

Two Text inside a ScrollView that Fit to the content of the Texts

I have two Text in a VStack. Both texts can grow to an unlimited amount of lines. I want that if the text grows more than the screen I can scroll and see all the content.
Mi first approach:
var body: some View {
VStack(alignment: .center, spacing: 0) {
VStack(alignment: .leading, spacing: 8) {
Text(title)
.font(.title)
Text(subtitle)
.font(.subheadline)
}
.frame(maxWidth: .infinity)
.background(Color.orange)
.padding(.top, 24)
.padding(.horizontal, 24)
Button(buttonTitle) { }
.frame(maxWidth: .infinity, minHeight: 50)
.background(Color.red)
.foregroundColor(.white)
.padding(.all, 24)
}.background(Color.gray)
.frame(maxWidth: UIScreen.main.bounds.size.width - 20)
}
Result:
Of course there is no scroll... so I added a ScrollView:
var body: some View {
VStack(alignment: .center, spacing: 0) {
ScrollView {
VStack(alignment: .leading, spacing: 8) {
Text(title)
.font(.title)
Text(subtitle)
.font(.subheadline)
}
.frame(maxWidth: .infinity)
.background(Color.orange)
.padding(.top, 24)
.padding(.horizontal, 24)
}
Button(buttonTitle) { }
.frame(maxWidth: .infinity, minHeight: 50)
.background(Color.red)
.foregroundColor(.white)
.padding(.all, 24)
}.background(Color.gray)
.frame(maxWidth: UIScreen.main.bounds.size.width - 20)
}
Result:
It works! But when the text is not enough to fill the whole height of the screen the ScrollView still expands to the whole space available and I want it to wrap the content and "disable" the scrolling capability.
Sample of what I get with little amount of text:
Sample of what I would like to get with little amount of text:
So the question is how can I make the ScrollView to "wrap" the content instead of expanding to the whole height of the parent View?

Can't seem to utilize Spacer() between text() and textfield()

I can't seem to get the Spacer() function to work when I'm within an HStack and trying to create space between my Text and Textfield views. The Spacer works to space out other areas of the view, but whenever I attempt to space between these two elements, it doesn't work.
Here is the code I'm working with:
VStack(alignment: .leading, spacing: 0) {
Rectangle()
.frame(height: 2, alignment: .top)
.foregroundColor(Color("grey2"))
Text("Tickets")
.kerning(0.5)
.scaledFont(name: "Gotham Medium", size: 18)
.foregroundColor(Color("grey4"))
.padding(.horizontal)
.padding(.bottom, 3)
.padding(.top, 35)
Rectangle()
.frame(height: 2, alignment: .top)
.foregroundColor(Color("grey2"))
HStack {
Text("Ticket URL")
.kerning(0.5)
.scaledFont(name: "Gotham Book", size: 16)
.foregroundColor(Color("spaceblack"))
Spacer()
TextField("Enter URL", text: $url)
}
.background(Color.blue)
.padding()
Rectangle()
.frame(height: 2, alignment: .top)
.foregroundColor(Color("grey2"))
}
.frame(maxWidth: .infinity)
.background(Color.red)
try adding .fixedSize() modifier to your TextField
TextField("Enter URL", text: $url)
.fixedSize()
or set a frame like so
TextField("Enter URL", text: $url)
.frame(width:200, height:50, alignment:.leading)
The problem is that TextField and Spacer() would take all available space and in this case TextField gets the priority; However, if you specify a fixed size or a frame to it then TextField won't stretch to take full space instead it will be fixed.
.fixedSize would allow your TextField to start small but eventually it will keep stretching the more text you write which can cause unwanted behavior.
.frame will fix your size to the provided width and hence there won't be any stretch and Spacer will have priority to take available space.

Aligning image to navigationBarTitle in SwiftUI

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]`

SwiftUI button image inset

Is there same thing as ImageEdgeInsets for SwiftUI. For example I have button 60x60. And want to make inset for image inside.
VStack(alignment: .leading, spacing: 0.0){
Section{
Button(action: {
if self.accountViewModel.signInContext == .quote{
self.presentationMode.wrappedValue.dismiss()
}else{
self.viewController?.dismiss(animated: true)
}
}){
Image("Close Icon").renderingMode(.template).padding(.vertical, 20).padding(.horizontal, 20).foregroundColor(.white).scaledToFit()
}
.frame(width: 60, height: 60)
Rectangle().line().padding(.horizontal, -30)
}
}
Yes, .padding is for that purpose, just needed to make image resizable. I suppose you need the following
Image("Close Icon")
.renderingMode(.template)
.resizable()
.aspectRatio(contentMode: .fit)
.padding(.vertical, 20).padding(.horizontal, 20)