SwiftUI GroupBoxStyle fill height - swiftui

I have a custom GroupBoxStyle where I would like to add a think coloured line to its left edge that takes the entire height of the GroupedBox. This GroupedBox is then used in rows in a VStack
Here in blue is the content of the GroupBox, in yellow is that line I want to add. The blue content can be any view.
In my my current implementation (see below) but the problem is that the vertical line does not take all the height of the GroupBox
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
VStack(spacing: 8) {
GroupBox {
HStack {
VStack {
Text("Hello")
Text("Hello")
Text("Hello")
Text("Hello")
}
Spacer()
}
}
GroupBox {
Color.blue
.frame(height: 50)
}
}
// try to uncomment this
// .padding(8)
}
.groupBoxStyle(StandardGroupBoxStyle())
.navigationBarTitle("My Group", displayMode: .large)
}
}
}
public struct StandardGroupBoxStyle: GroupBoxStyle {
public func makeBody(configuration: Self.Configuration) -> some View {
HStack(spacing: 0) {
Color.yellow
.frame(width: 5)
.frame(maxHeight: .infinity)
VStack {
configuration
.content
}
.border(Color.green)
Spacer()
}
.border(Color.red)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
This implementation and example creates this (note the yellow not taking the entire height of the row.

Here is a solution (I kept original extra modifiers, but assume they are for debug purpose). Tested with Xcode 12.1 / iOS 14.1
public struct StandardGroupBoxStyle: GroupBoxStyle {
public func makeBody(configuration: Self.Configuration) -> some View {
HStack(spacing: 0) {
configuration.content
.padding(.leading, 5)
.border(Color.green) // << debug?
Spacer() // << debug?
}
.overlay(Color.yellow.frame(width: 5), alignment: .leading)
.border(Color.red) // << debug?
}
}

Related

Color behind the time on iOS

This is my code:
struct Account: View {
var body: some View {
VStack {
ScrollView {
HStack {
Text("Account")
.font(.largeTitle)
.fontWeight(.bold)
Spacer(minLength: 0)
}
.padding()
.background(Color.indigo)
VStack {
Text("Doe, John Jack")
.font(.title)
Divider()
.foregroundColor(Color.indigo)
HStack {
Text("")
}
}
Spacer(minLength: 0)
VStack {
Button(action: {
}) {
Text("Log Out")
.foregroundColor(.red)
.fontWeight(.bold)
}
}
}
}
}
}
If you run the code above, you will see that the indigo doesn't go behind the time and battery precentage. How can I make it do that?
Try like this:
struct Account: View {
var body: some View {
VStack {
ScrollView {
HStack {
Text("Account")
.font(.largeTitle)
.fontWeight(.bold)
Spacer(minLength: 0)
}
.padding()
.background(Color.indigo)
VStack {
Text("Doe, John Jack")
.font(.title)
Divider()
.foregroundColor(Color.indigo)
HStack {
Text("")
}
}
Spacer(minLength: 0)
VStack {
Button(action: {
}) {
Text("Log Out")
.foregroundColor(.red)
.fontWeight(.bold)
}
}
}.padding(.top, 66)
}
.background(Color.indigo)
.edgesIgnoringSafeArea(.all)
}
}
You would also have to add some top padding to the ScrollView since ignoring safe area is going to push all your views to the margins
You should ignore the top safe area for this:
.ignoresSafeArea(.container, edges: .top)
Full working example
struct ContentView: View {
var body: some View {
Color
.indigo
.ignoresSafeArea(.container, edges: .top)
}
}

Utilize HStack to confirm to logo and skip text

Does anyone know how to make the following view in SwiftUI?
HStack:
[ blank logo(centered) Skip(text)]
So I have the following HStack:
Zstack(alignment: .topLeading) {
VStack{
HStack {
Image("onboarding-logo")
.resizable()
.scaledToFit()
.frame(width: 150.0, height: 150.0)
.padding(.top, 35)
}
}
}
Does anyone know how I can have a "Skip" text in the top right corner of the screen, but also keep my logo centered and not have anything on the left side? I've tried Spacers and all, but I'm having no luck.
I would like to click "Skip" and then lead to another view.
There are many ways to achieve this. Here I have used LazyVGrid since other answers based on Stacks
struct ContentView: View {
var body: some View {
VStack {
LazyVGrid(columns: [GridItem(), GridItem(), GridItem()], content: {
Spacer()
Image(systemName: "star.fill")
.frame(width: 150, height: 150, alignment: .center)
.border(.gray)
Button(action: {
}, label: {
Text("Skip")
})
})
.border(.gray)
Spacer()
}
}
}
Is this the screen configuration you want?
The Zstack is used to center the Hstack into which the image is placed, and the new HStack uses a spacer to move the Text to the right.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
VStack {
HStack(alignment: .center) {
Image("farnsworth")
.resizable()
.scaledToFit()
.frame(width: 150.0, height: 150.0)
}
Spacer()
}
VStack {
HStack {
Spacer()
Button {
// do Something...
} label: {
Text("Skip>>")
.padding(.top, 10)
}
}
Spacer()
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Cannot remove unwanted space from this VStack

I really cannot figure out what is pushing my Detail HStack View down to the bottom of the page. I've tried adding spacers and other tweaks but no success. You'll find the entire code below, its compile-ready as is.
import SwiftUI
struct ContentView: View {
var body: some View {
GeometryReader { geo in
VStack() {
VideoPlayerView()
.frame(width: geo.size.width, alignment: .center)
CategoryScrollView()
Spacer()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct CategoryScrollView: View {
var body: some View {
HStack {
VStack {
Text("Category")
.font(.system(size: 30))
}
Divider()
VStack {
Text("Chapter")
.font(.system(size: 30))
}
Divider()
VStack {
Text("Lesson")
.font(.system(size: 30))
}
}.frame(height: 100)
}
}
struct CategoryScrollView_Previews: PreviewProvider {
static var previews: some View {
CategoryScrollView()
}
}
struct VideoPlayerView: View {
var body: some View {
GeometryReader { geo in
ZStack(alignment: .center) {
Rectangle()
.frame(height: geo.size.width / 1.4)
.cornerRadius(50)
Image(systemName: "play.fill")
.resizable()
.foregroundColor(.blue)
.frame(width: geo.size.width / 5,height: geo.size.width / 4)
}
}
}
}
struct VideoPlayerView_Previews: PreviewProvider {
static var previews: some View {
VideoPlayerView()
}
}
If unconstrained, the GeometryReader takes all available space.
You need to set the height of the VideoPlayerView as well:
struct ContentView: View {
var body: some View {
GeometryReader { geo in
VStack {
VideoPlayerView()
.frame(width: geo.size.width, height: geo.size.height / 4, alignment: .center)
CategoryScrollView()
Spacer()
}
}
}
}
Also, you can remove frame(height: 100) from the CategoryScrollView:
struct CategoryScrollView: View {
var body: some View {
HStack {
VStack {
Text("Category")
.font(.system(size: 30))
}
Divider()
VStack {
Text("Chapter")
.font(.system(size: 30))
}
Divider()
VStack {
Text("Lesson")
.font(.system(size: 30))
}
}
// .frame(height: 100) // remove this
}
}
If necessary, you can specify this in the parent view using GeometryReader instead of hardcoding values.
I would add this frame to the GeometryReader.
GeometryReader { geo in
VStack() {
VideoPlayerView()
.frame(width: geo.size.width, alignment: .center)
CategoryScrollView()
Spacer()
}.frame(maxHeight:geo.size.height/2)
}

Change GroupBox background color in SwiftUI

How can I change the default gray background color of a GroupBox view in SwiftUI?
I tried adding a background modifier, but this just changes the white background underneath the box (see screenshot).
GroupBox(label: Text("Label"), content: {
Text("Content")
})
.background(Color.blue)
This is default group box style. You can create whatever group box needed using custom style.
Here is an example. Tested with Xcode 12 / iOS 14.
struct DemoGroupBox: View {
var body: some View {
GroupBox(label: Text("Label"), content: {
Text("Content")
})
.groupBoxStyle(TransparentGroupBox())
.padding()
}
}
struct TransparentGroupBox: GroupBoxStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.content
.frame(maxWidth: .infinity)
.padding()
.background(RoundedRectangle(cornerRadius: 8).fill(Color.blue))
.overlay(configuration.label.padding(.leading, 4), alignment: .topLeading)
}
}
This problem can be solved in this way
Tested on iOS 16, Xcode 14.0 Beta
struct TestView: View {
var body: some View {
GroupBox(label: Text("Label"), content: {
Text("Content")
})
.groupBoxStyle(ColoredGroupBox())
.padding()
}
}
struct ColoredGroupBox: GroupBoxStyle {
func makeBody(configuration: Configuration) -> some View {
VStack {
HStack {
configuration.label
.font(.headline)
Spacer()
}
configuration.content
}
.padding()
.background(RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(.blue)) // Set your color here!!
}
}
Result:
Full code for above result
struct TestView: View {
var body: some View {
VStack {
Divider()
HStack {
Text("Asperi's solution")
Text("(Problematic with label)")
.bold()
}
GroupBox(label: Text("Label"), content: {
Text("Content")
})
.groupBoxStyle(TransparentGroupBox())
.padding()
Divider()
Text("My Solution")
GroupBox(label: Text("Label"), content: {
Text("Content")
})
.groupBoxStyle(ColoredGroupBox())
.padding()
Divider()
Text("Default")
GroupBox(label: Text("Label"), content: {
Text("Content")
})
.padding()
Divider()
}
}
}
struct TransparentGroupBox: GroupBoxStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.content
.frame(maxWidth: .infinity)
.padding()
.background(RoundedRectangle(cornerRadius: 8).fill(Color.blue))
.overlay(configuration.label.padding(.leading, 4), alignment: .topLeading)
}
}
struct ColoredGroupBox: GroupBoxStyle {
func makeBody(configuration: Configuration) -> some View {
VStack {
HStack {
configuration.label
.font(.headline)
Spacer()
}
configuration.content
}
.padding()
.background(RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(.blue)) // Set your color here!!
}
}
Short answer: it is currently not properly supported by SwiftUI :(
Longer answer: you can create a custom GroupBoxStyle (see example) which will allow you to change the background... but at the cost of having to then manually layout the contents (which somewhat defeats the purpose).

SwiftUI how to set image for NavigationBar titleView like in UIKit?

I want to set an image in the titleView of NavigationBar in SwiftUI, as we do in UIKit
navigationItem.titleView = UIImageView(image: UIImage(named: "logo"))
this is how we do it in UIKit.
anyone know how to do it?
Here's how to do it:
Add SwiftUIX to your project.
Set your custom title view via View.navigationBarTitleView(_:displayMode:)
Example code:
struct ContentView: View {
public var body: some View {
NavigationView {
Text("Hello World")
.navigationBarTitleView(MyView())
}
}
}
Simple, Just add your root view into ZStack with top alignment and add your custom center view after root view
struct CenterNavigattionBar: View {
var body: some View {
ZStack(alignment: .top){
//Root view with empty Title
NavigationView {
Text("Test Navigation")
.navigationBarTitle("",displayMode: .inline)
.navigationBarItems(leading: Text("Cancle"), trailing: Text("Done"))
}
//Your Custom Title
VStack{
Text("add title and")
.font(.headline)
Text("subtitle here")
.font(.subheadline)
}
}
}
}
Before Image
After Image
Just use a toolbar.
You can add any views
import SwiftUI
struct HomeView: View {
// MARK: - Initializer
init() {
let appearance = UINavigationBar.appearance()
appearance.isOpaque = true
appearance.isTranslucent = false
appearance.barTintColor = UIColor(named: "background")
appearance.shadowImage = UIImage()
}
// MARK: - View
// MARK: Public
var body: some View {
NavigationView {
VStack(spacing: 20) {
Text("Hello")
Text("Navigation Bar Test")
}
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading: leadingBarButtonItems, trailing: trailingBarButtonItems)
.toolbar {
ToolbarItem(placement: .principal) {
VStack {
Text("Title").font(.headline)
Text("Subtitle").font(.subheadline)
}
}
}
}
}
// MARK: Private
private var leadingBarButtonItems: some View {
Button(action: {
}) {
Text("Left Button")
.font(.system(size: 12, weight: .medium))
}
}
private var trailingBarButtonItems: some View {
HStack {
Button(action: {
}) {
Text("R1\nButton")
.font(.system(size: 12, weight: .medium))
.multilineTextAlignment(.center)
}
Button(action: {
}) {
Text("R2\nButton")
.font(.system(size: 12, weight: .medium))
.multilineTextAlignment(.center)
}
}
}
}
Currently, you can't.
There are two overloads for .navigationBarTitle(), taking either a Text view or a type conforming to StringProtocol. You can't even pass in a modified view like Text("Title").font(.body). This would be a great feature, I'd submit a feature request: http://feedbackassistant.apple.com
Maybe this works for you?
Basically:
Use GeometryReader to get the width of the screen
Have NavigationBarItems(leading: HStack {Spacer() Image("name").resizable().frame(width:..., height: ..., alignment: .center Spacer()}.frame(width:geometry.size.width)
Example code:
struct ContentView: View {
var body: some View {
NavigationView {
GeometryReader { geometry in
Text("Hello, world!")
.padding()
.navigationTitle("test")
.navigationBarItems(leading: HStack {
Spacer()
Image("money")
.resizable()
.frame(width: 50, height: 50, alignment: .center)
Spacer()
}
.frame(width: geometry.size.width)
)
}
}
}
}
Try this...
How to put a logo in NavigationView in swiftui?
This shows how to handle adding an Image to NavigationView in SwiftUI. Hope it helps.