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
Related
Using the new SwiftUI Charts framework, we can make a chart bigger than the visible screen and place it into a ScrollView to make it scrollable. Something like this:
var body : some View {
GeometryReader { proxy in
ScrollView(.horizontal, showsIndicators: false) {
Chart {
ForEach(data) { entry in
// ...
}
}
.frame(width: proxy.size.width * 2)
}
}
}
Does anybody know if it is possible to programmatically move the scroll to display a certain area of the chart?
I've tried using ScrollViewReader, setting the IDs at the x-axis labels, and trying to use the scrollTo function to navigate to any of those positions with no luck:
Chart {
/// ...
}
.chartXAxis {
AxisMarks(values: .stride(by: .day)) { value in
if let date : Date = value.as(Date.self) {
Text(date, style: .date)
.font(.footnote)
}
}
}
This cheesy workaround seems to do the trick. I put the chart in a ZStack with an HStack overlaying the chart. The HStack contains a bunch of invisible objects that conform to the Identifiable protocol. The quantity, ids, and positions of the invisible objects match the charted data.
Since the ZStack view now contains identifiable elements, ScrollViewReader works as expected.
import SwiftUI
import Charts
struct ChartData: Identifiable {
var day: Int
var value: Int
var id: String { "\(day)" }
}
struct ContentView: View {
#State var chartData = [ChartData]()
#State var scrollSpot = ""
let items = 200
let itemWidth: CGFloat = 30
var body: some View {
VStack {
ScrollViewReader { scrollPosition in
ScrollView(.horizontal) {
// Create a ZStack with an HStack overlaying the chart.
// The HStack consists of invisible items that conform to the
// identifible protocol to provide positions for programmatic
// scrolling to the named location.
ZStack {
// Create an invisible rectangle for each x axis data point
// in the chart.
HStack(spacing: 0) {
ForEach(chartData) { item in
Rectangle()
.fill(.clear)
// Setting maxWidth to .infinity here, combined
// with spacing:0 above, makes the rectangles
// expand to fill the frame specified for the
// chart below.
.frame(maxWidth: .infinity, maxHeight: 0)
// Here, set the rectangle's id to match the
// charted data.
.id(item.id)
}
}
Chart(chartData) {
BarMark(x: .value("Day", $0.day),
y: .value("Amount", $0.value),
width: 20)
}
.frame(width: CGFloat(items) * itemWidth, height: 300)
}
}
.padding()
.onChange(of: scrollSpot, perform: {x in
if (!x.isEmpty) {
scrollPosition.scrollTo(x)
scrollSpot = ""
}
})
}
.onAppear(perform: populateChart)
Button("Scroll") {
if let x = chartData.last?.id {
print("Scrolling to item \(x)")
scrollSpot = x
}
}
Spacer()
}
}
func populateChart() {
if !chartData.isEmpty { return }
for i in 0..<items {
chartData.append(ChartData(day: i, value: (i % 10) + 2))
}
}
}
IMHO this should work out of the SwiftUI box. Apple's comments for the initializer say it creates a chart composed of a series of identifiable marks. So... if the marks are identifiable, it is not a stretch to expect ScrollViewReader to work with the chart's marks.
But noooooo!
One would hope this is an oversight on Apple's part since the framework is new, and they will expose ids for chart marks in an upcoming release.
I am using Xcode to create an app that requires buttons. Right now, when I create a button, I get the text label, which I want, but I also get a background with rounded corners around it. I want to have the button with just the label but without the background. I was using Swift Playgrounds before Xcode and did not have this problem.
Here is my code:
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
Rectangle()
.frame(width: 1000, height: 500)
.foregroundColor(.red)
Button(action: {
}) {
Text("Button")
}
}
}
}
Customize the button's style with the .buttonStyle view modifier:
Button(action: {}) {
Text("Button")
}
.buttonStyle(.borderless)
.borderless, .plain, and .link are the options that will result in no border.
Here is a Button without any background or border.
Button("Click") {
//do something
}
.background(.clear) //this
I'm drawing icons on a toolbar with a material background. The Text and symbol Images are white, but if I draw my own Path, it's gray.
struct ContentView: View {
var body: some View {
HStack {
Text("Hi")
Image(systemName: "square.and.arrow.up.fill")
Path { p in
p.addRect(CGRect(origin: .zero, size: .init(width: 20, height: 30)))
}.fill()
.frame(width: 20, height: 30)
}
.padding()
.background(.regularMaterial)
}
}
I get the same result with .fill(), .fill(.foreground), or .fill(.primary).
Why is it gray? How do I get it to match the white text color?
I find it weird that .white or .black work, but .primary doesn't.
Upon discovering the Material documentation, I found this interesting snippet:
When you add a material, foreground elements exhibit vibrancy, a context-specific blend of the foreground and background colors that improves contrast. However using foregroundStyle(_:) to set a custom foreground style — excluding the hierarchical styles, like secondary — disables vibrancy.
Seems like you have to force a different color (see previous edit which I used the environment color scheme), since hierarchical styles such as .primary won't work by design.
Luckily there is a way around this - you can use colorMultiply to fix this problem. If you set the rectangle to be .white, then the color multiply will make it the .primary color.
Example:
struct ContentView: View {
var body: some View {
HStack {
Text("Hi")
Image(systemName: "square.and.arrow.up.fill")
Path { p in
p.addRect(CGRect(origin: .zero, size: .init(width: 20, height: 30)))
}
.foregroundColor(.white)
.colorMultiply(.primary)
.frame(width: 20, height: 30)
}
.padding()
.background(.regularMaterial)
}
}
There is no issue with code, your usage or expecting is not correct! Text and Image in that code has default Color.primary with zero code! So this is you, that messing with .fill() you can delete that one!
struct ContentView: View {
var body: some View {
HStack {
Text("Hi")
Image(systemName: "square.and.arrow.up.fill")
Path { p in
p.addRect(CGRect(origin: .zero, size: .init(width: 20, height: 30)))
}
.fill(Color.primary) // You can delete this line of code as well! No issue!
.frame(width: 20, height: 30)
}
.padding()
.background(Color.secondary.cornerRadius(5))
}
}
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.
If I create this simple ScrollView containing 100 rows and create a button to scroll to row 60, I would like to highlight that I am at row 60, maybe with a background colour or similar. I cannot figure out how to do this.
var body: some View {
ScrollView {
ScrollViewReader { value in
Button("Jump to #60") {
value.scrollTo(60, anchor: .center)
}
ForEach(0..<100) { i in
Text("Line \(i)")
}
}
}
}
Here is possible approach - you introduce state for highlighted row, which can modify by needs, including for scroll to
Tested with Xcode 12b5 / iOS 14
#State private var highlighted: Int?
var body: some View {
ScrollView {
ScrollViewReader { value in
Button("Jump to #60") {
value.scrollTo(60, anchor: .center)
highlighted = 60
}
ForEach(0..<100) { i in
Text("Line \(i)")
.frame(maxWidth: .infinity)
.background(i == highlighted ? Color.gray : Color.clear)
}
}
}
}
Found a way.
Create a function to set a colour if the current index and the scrollTo target are equal.
func highlight(index: Int, target: Int) -> Color {
if target == index {
return .black
} else {
return .clear
}
Then use it to set background, border etc.
Text("Line \(i)")
.border(highlight(index: i, target: 60), width: 5)