Is there a way to "activate" word-wrapping in SwiftUI? - swiftui

Is there an alternative to UIKit's LineBreakMode in SwiftUI?
Or are there any alternatives to get the Text truncated via words not via characters in SwiftUI?
I've tried the
.fixedSize(horizontal: false, vertical: true)
approach but it does not seem to work.
Update with the code:
VStack {
HStack {
CustomText("Some Text")
Spacer()
SomeOtherView()
}.padding(.horizontal, 16)
}
Update:
struct CustomText: View {
// MARK: - Properties
let text: String
let color: Color?
// MARK: - Init
init(text: String, color: Color? = Colors.darkGrey) {
self.text = text
self.color = color
}
// MARK: - Body
var body: some View {
Text(text)
.tracking(0.256)
.fontWithLineHeight(font: Fonts.header2, lineHeight: 33)
.foregroundColor(color)
.minimumScaleFactor(0.5)
.lineLimit(nil)
}
}
Some other view is just a Lottie Animation View so it should not make any difference.
An observation is that SwiftUI prefers to add hyphenation rather than minimising the font => I think that the priority of hyphenation is bigger than the minimumScaleProperty.

Related

fixed text inside TextField SwiftUI

i want to put some fixed text inside TextField like "email: ....."
I tryed this:
HStack {
Image(systemName: "list.bullet").foregroundColor(.gray)
Text("email:")
TextField("", text: Binding(
get: { viewModel.email },
set: { viewModel.email = $0 }
))
.textFieldStyle(DefaultTextFieldStyle())
.frame(width: 60)
}
.padding()
.overlay(RoundedRectangle(cornerRadius: 10).stroke(
viewModel.emailValid() ? Color.gray : Color.red, lineWidth: 1
))
The problem is that this implementation only allows to click in specific part (inside the textField space) but i want to make all clickable to be more friendly to edit.
I tryed putting TextField and Text inside ZStack but the text entered by the user in inside te "Email text".
Maybe its possible to move the textField cursor to the end of the "email" text, or another implementation?
Thanks!
So if its mainly about activating the TextField on tap/click on any area of the element, you can use programmatic focus for TextField:
struct ContentView: View {
#State var input = ""
#FocusState var focused: Bool
var body: some View {
HStack {
Image(systemName: "list.bullet").foregroundColor(.gray)
Text("email:")
TextField("", text: $input)
.frame(width: 80)
.focused($focused)
}
.padding()
.overlay(RoundedRectangle(cornerRadius: 10).stroke(
true ? Color.gray : Color.red, lineWidth: 1
))
.contentShape(Rectangle()) // makes the whole element tappable
.onTapGesture {
focused = true // sets focus for textField
}
}
}

SwiftUI marquee sample

I am trying to find swiftUI code implementing marquee which shows only the rolling text content inside the text view. Text rolling outside to the left and right of the text view will not be shown.
Put the text in an overlay of your box and animate the .offset to scroll the text. Add .clipped() to the box to clip any text that appears outside of the box:
struct ContentView: View {
#State private var textoffset = 300.0
let text = "This is a test of scrolling text. This is only a test."
var body: some View {
Color.yellow
.frame(width: 250, height: 100)
.overlay (
Text(text)
.fixedSize()
.offset(x: textoffset, y: 0)
)
.animation(.linear(duration: 10)
.repeatForever(autoreverses: false), value: textoffset)
.clipped()
.onAppear {
textoffset = -300.0
}
}
}

How to Remove Padding/Align Picker Value Left with Hidden Label in SwiftUI?

I'm trying to align my Picker hidden label values left to another TextField in my form (see attached image). What's the best way to remove the 8px padding in the displayed picker value?
import SwiftUI
struct ContactFormView: View {
var countries = ["Malaysia", "Singapore", "Japan"]
#State var name: String = ""
#State var mobile: String = ""
#State var mobileCountry: String = "Malaysia"
var body: some View {
NavigationView {
Form {
Section(header: Text("Details")) {
TextField("Name", text: $name)
.border(Color.red, width: 1)
HStack {
Picker(selection: $mobileCountry, label: EmptyView()) {
ForEach(countries, id: \.self) {
Text($0).border(Color.red)
}
}
.scaledToFit()
.labelsHidden()
.border(Color.red, width: 1)
TextField("Mobile", text: $mobile)
.border(Color.red, width: 1)
}
}
}
.navigationBarTitle("New contact")
}
}
}
Probably your best bet is to use a negative padding on your picker:
Picker(selection: $mobileCountry, label: EmptyView()) {
ForEach(countries, id: \.self) {
Text($0).border(Color.red)
}
}
.scaledToFit()
.labelsHidden()
.border(Color.red, width: 1)
.padding(.horizontal, -8)
Loading your code in Xcode 12.5, adding that negative padding aligns the text in the way you want.
Intuitively I would have chosen .padding(.leading, -8) to remove padding for the Picker's leading edge only – but in doing so, the gap between the picker and text field grows larger.
Applying the negative padding to both horizontal values keeps that gap the same for me. But I'd recommend in your code trying both, and seeing which one makes the most sense for you.

SwiftUI style update onEditChanged does not pop up keyboard

I am trying to update the style of the HStack containing my text box when the Textbox is selected. In the example below, I want the text box to have a red border when selected, otherwise the border should be gray.
My issue is that the textbox seems to go through an intermediate transition that I don't want, which is the border is updated to red, but the keyboard doesn't pop up until I select the textbox again (The textbox moves up a bit and then goes back down). It seems that there is some issue with the ordering of how the view refresh happens.
#State private var text: String
#State private var textFieldSelected: Bool = false
var body: some View {
let stack = HStack {
TextField("Enter name", text: $text, onEditingChanged: {
(changed) in
textFieldSelected = changed
})
}
if (textFieldSelected) {
stack
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.red, lineWidth: 1))
} else {
stack
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 1))
}
}
Here's a video example of the existing behavior:
Make it even simpler by using ternary condition for the border and the issue won't appear
struct ContentView : View {
#State private var text: String = ""
#State private var textFieldSelected: Bool = false
var body: some View {
HStack {
TextField("Enter name", text: $text, onEditingChanged: {
(changed) in
textFieldSelected = changed
})
}
.overlay(RoundedRectangle(cornerRadius: 10).stroke(textFieldSelected ? Color.red : Color.gray, lineWidth: 1))
}
}
Tested on iPhone 8 Plus iOS 14

SwiftUI grid with column that fits content?

Is this layout possible with SwiftUI?
I want the first column to wrap the size of the labels, so in this case it will be just big enough to show "Bigger Label:". Then give the rest of the space to the second column.
This layout is pretty simple with auto layout.
SwiftUI 2020 has LazyVGrid but the only ways I see to set the column sizes use hardcoded numbers. Do they not understand what a problem that causes with multiple languages and user-adjustable font sizes?
It is not so complex if to compare number of code lines to make this programmatically in both worlds...
Anyway, sure it is possible. Here is a solution based on some help modifier using view preferences feature. No hard. No grid.
Demo prepared & tested with Xcode 12 / iOS 14.
struct DemoView: View {
#State private var width = CGFloat.zero
var body: some View {
VStack {
HStack {
Text("Label1")
.alignedView(width: $width)
TextField("", text: .constant("")).border(Color.black)
}
HStack {
Text("Bigger Label")
.alignedView(width: $width)
TextField("", text: .constant("")).border(Color.black)
}
}
}
}
and helpers
extension View {
func alignedView(width: Binding<CGFloat>) -> some View {
self.modifier(AlignedWidthView(width: width))
}
}
struct AlignedWidthView: ViewModifier {
#Binding var width: CGFloat
func body(content: Content) -> some View {
content
.background(GeometryReader {
Color.clear
.preference(key: ViewWidthKey.self, value: $0.frame(in: .local).size.width)
})
.onPreferenceChange(ViewWidthKey.self) {
if $0 > self.width {
self.width = $0
}
}
.frame(minWidth: width, alignment: .trailing)
}
}