I am attempting to customize the font size of the two tags inside the Picker. I am using the SegmentedPickerStyle and I would like the Picker font size to match Text size of the other titles/strings that I am using on my form. The other fonts are using .font(.headline)
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading){
Group {
Picker("", selection: $form.nameType) {
Text("First/Last name \(form.nameType == 0 ? "*" : "")").tag(0)
Text("Company name \(form.nameType == 1 ? "*" : "")").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
}
}
}
}
}
You can customize the font of your segment controllers adding this:
init() {
UISegmentedControl.appearance().setTitleTextAttributes(
[
.font: UIFont.systemFont(ofSize: 18),
], for: .normal)
}
To expand on #Ludyem's correct answer, if in addition you'd like your SwiftUI SegmentedPickerStyle to support Dynamic Type fonts, then this should do the trick:
let font = UIFont.preferredFont(forTextStyle: .footnote) // regular weight
UISegmentedControl.appearance().setTitleTextAttributes([.font: UIFont.systemFont(ofSize: font.pointSize, weight: .medium)], for: .normal) // medium weight
The default font for a UISegmentedControl appears to be systemFont(ofSize: 13) - which correspond to UIFont.TextStyle.footnote - but medium weight. So the above gets the scaled (13pt) footnote font, and then adjust the weight to match.
Related
I want to create the following design in SwiftUI. I am currently using a list and creating a section that contains cells like so.
List {
Section {
ForEach(titles) { title in
Cell(title: title)
}
}
}
When I apply a modifier like a border to the section it applies it to all the views contained in the Section. I want to have that border around the entire Section with a corner radius of 10. The closest I have got to creating the desired design is by not using a List but instead using a VStack and applying the following modifiers
VStack {
ForEach(titles) { title in
Cell(title: title)
}
}
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(.gray, lineWidth: 2))
I discovered however that this is not a smart approach as the List uses reusable cells and in the case of VStack they do not. Is it possible to create the wanted design with a List in SwiftUI? I do not want to opt for the default list style provided by Apple
Just Copy paste this code and customise it as per your needs, enjoy
import SwiftUI
struct CustomizeListView: View {
var titles = ["First Section" : ["Manage your workout", "View recorded workouts", "Weight tracker", "Mediation"], "Second Section" : ["Your workout", "Recorded workouts", "Tracker", "Mediations"]]
var body: some View {
List {
ForEach(titles.keys.sorted(by: <), id: \.self){ key in
Section(key) {
VStack(alignment: .leading, spacing: 0){
ForEach(titles[key]!, id: \.self) { title in
HStack{
Text(title)
Spacer()
Image(systemName: "arrow.right")
}//: HSTACK
.padding(20)
Divider()
}//: LOOP
}//: VSTACK
.overlay(
RoundedRectangle(cornerRadius: 10, style: .circular).stroke(Color(uiColor: .tertiaryLabel), lineWidth: 1)
)
.foregroundColor(Color(uiColor: .tertiaryLabel))
}//: SECTION
}//: LOOP
}//: LIST
.listStyle(InsetListStyle())
}
}
struct CustomizeListView_Previews: PreviewProvider {
static var previews: some View {
CustomizeListView()
}
}
I assume you just need to change list style, like
List {
Section {
ForEach(titles) { title in
Cell(title: title)
}
}
}
.listStyle(.insetGrouped) // << here !!
I am using GeometryReader to calculate the size of font to use to fit several lines of text, however it is always too big. What can be wrong with my calculation?
Here's a simply Playground
import SwiftUI
import PlaygroundSupport
struct MainView : View {
var body: some View {
VStack {
SomeView()
.frame(width: 508.0, height: 246.5)
}
}
}
struct SomeView: View {
let newData = ["1", "2", "3", "4", "5", "6", "7", "8"]
var lines: Int { newData.count }
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading, spacing: 0) {
ForEach(0..<lines) { idx in
Text(newData[idx])
.padding(0)
}
}
.font(.system(size: geometry.size.height / CGFloat(lines)))
.onAppear {
print(geometry.size)
print(lines)
print(geometry.size.height / CGFloat(lines))
}
}
}
}
PlaygroundPage.current.setLiveView(MainView())
This gives me this result:
The GeometryReader correctly reports the height of the box to be 246.5 and the number of lines to be 8 which results in a 30.8125 font size.
It seems the font size needs some additional padding but how do I calculate this?
I want to be able to supply an unknown amount of data and have it fill the box. (Well I am actually using a LazyVGrid which is suffering the same issue).
Font size is not equal to size this text takes on the screen. Check out this answer
You can get real line height with lineHeight from UIFont, and using this value you can calculate font size needed for your line height:
let expectedLineHeight = geometry.size.height / CGFloat(lines)
let fontSize = expectedLineHeight * expectedLineHeight / UIFont.systemFont(ofSize: expectedLineHeight).lineHeight
...
.font(.system(size: fontSize))
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.
I am creating a reusable gallery view for an app and am having difficulties when any picture is tapped it suppose to become full screen but only the first picture in the array is shown every time no matter the picture tapped. Below is my code, thanks.
import SwiftUI
struct ReusableGalleryView: View {
let greenappData: GreenAppNews
let gridLayout: [GridItem] = Array(repeating: GridItem(.flexible()), count: 3)
#State private var fullscreen = false
#State private var isPresented = false
var body: some View {
VStack{
ScrollView{
LazyVGrid(columns: gridLayout, spacing: 3) {
ForEach(greenappData.GreenAppGallery, id: \.self) { item in
Image(item)
.resizable()
.frame(width: UIScreen.main.bounds.width/3, height: 150)
.onTapGesture {
self.isPresented.toggle()
print(" tapping number")
}
.fullScreenCover(isPresented: $isPresented) {
FullScreenModalView( imageFiller: item)
}
.background(Color.gray.opacity(0.5))
}
}
.padding(.horizontal)
}
}
}
}
This is an example of the json data:
{
"id" : "1",
"GreenAppGallery" : [
"Picture-1",
"Picture-2",
"Picture-3",
"Picture-4",
"Picture-5",
"Picture-6",
"Picture-7",
"Picture-8",
"Picture-9",
"Picture-10"
]
},
fullScreenCover, like sheet tends to create this type of behavior in iOS 14 when using isPresented:.
To fix it, you can change to the fullScreenCover(item: ) form.
Not having all of your code, I'm not able to give you an exact version of what it'll look like, but the gist is this:
Remove your isPresented variable
Replace it with a presentedItem variable that will be an optional. Probably a datatype that is in your gallery. Note that it has to conform to Identifiable (meaning it has to have an id property).
Instead of toggling isPresented, set presentedItem to item
Use fullScreenCover(item: ) { presentedItem in FullScreenModalView( imageFiller: presentedItem) } and pass it your presentedItem variable
Move the fullScreenCover so that it's attached to the ForEach loop rather than the Image
Using this system, you should see it respond to the correct item.
Here's another one of my answers that covers this with sheet: #State var not updated as expected in LazyVGrid
I am creating a scrollView with a code like this:
ScrollView {
ForEach(items) { item in
VStack {
DisclosureGroup {
SubElements()
}
label: {
DisplayItem(item)
}
}
}
}
The result is something like this:
One thing I don't like about this is the chevron color. I want it black.
How do I change that?
Any ideas?
You can change the chevron color with adding .accentColor after your DisclosureGroup.
Accent color is a color that represents the system or application accent color. See the Apple docs
For your first issue it is probably down to padding that is stopping the indicator from being inset enough. As you haven't included all your code, it's difficult to tell where to put it but on your ScrollView should be enough.
For the color of the indicator, you can just use the accentColor modifier, and set the color that you want.
Here is a very simple example.
struct ContentView: View {
var body: some View {
ScrollView {
ForEach(0..<5) { _ in
DisclosureGroup(
content: { Text("Content") },
label: { Text("Label") }
)
.accentColor(.black)
}
}.padding(.horizontal, 30)
}
}
This is what it looks like
Change the color of content with foregroundColor and color of the arrow with accentColor:
DisclosureGroup {
HStack {
Text(faqElement.answer)
.adaptiveFont(size: 14, family: .app, style: .regular)
.foregroundColor(Color.App.Dynamic.Gray.value_900) // <-- Color of the text in content
Spacer()
}
.padding(.vertical)
} label: {
HStack {
Text(faqElement.question)
.adaptiveFont(size: 20, family: .app, style: .regular)
.foregroundColor(Color.App.Dynamic.Gray.value_900) // <-- Color of the label
Spacer()
}
.frame(height: 34)
}
.accentColor(Color.App.Static.Green.value_700) // <-- Color of the arrow