Change UIView background color in SwiftUI - ios

Hello everyone. I'm creating a simple iOS app with SwiftUI, and I'd like to change my view's background color to a custom one I have.
This is something extremely easy to do but it seems that it's impossible to achieve in SwiftUI without using ZStacks or workarounds like that, which if you use a List, for example, don't work.
I want to change the color of the view, not use a ZStack with a custom color and then put the rest of the views on top of it. I tried using UIView.appearance().backgroundColor = color when initializing my view, but then all the view is hidden and the screen is filled with the color chosen.
As I'm not good at explaining, here you have some images describing the problem:
Without color change
With color change
My code
import SwiftUI
struct TabController: View {
#State private var selection = 0
init() {
UIView.appearance().backgroundColor = UIColor(named: "backgroundColor")
}
var body: some View {
TabView(selection: $selection) {
HomePageView()
.tabItem {
Image(systemName: "house.fill")
.font(.title)
}
.tag(0)
Text("Second View")
.font(.title)
.tabItem {
Image(systemName: "bell.fill")
.font(.title)
}
.tag(1)
}.edgesIgnoringSafeArea(.top)
}
}

Hope this will help to understand:
var body: some View {
Color.purple
.overlay(
VStack(spacing: 20) {
Text("Overlay").font(.largeTitle)
Text("Example").font(.title).foregroundColor(.white)
})
.edgesIgnoringSafeArea(.vertical)
}
Another If you use the views in Group
var body: some View {
Group {
Text("Hello SwiftUI!")
}
.background(Color.black)
}

For changing the background color, I think the current method most people, and myself, are using is using a ZStack. I haven't seen many problems with putting a UIViewRepresentable on top of it.
var body: some View {
ZStack {
Color.blue
.edgesIgnoringSafeArea(.all)
VStack {
Text("Hello World!")
}
}
}

Related

How to make SwiftUI Form translucent?

I am trying to make the SwiftUI Form translucent. I've tried applying .background(.thinMaterial) modifier to the Form. However, this changes the look of the scroll view background. I'd like to apply translucency to the area that is white in the picture I attached.
Is there a way to do it? I am developing for iOS 16.
var body: some View {
NavigationStack(path: $path) {
ZStack {
LinearGradient(gradient: Gradient(colors: [.pink, .yellow]),
startPoint: .topTrailing,
endPoint: .bottomLeading)
.edgesIgnoringSafeArea(.all)
Form {
VStack {
...
}
}.scrollContentBackground(.hidden)
}
}
It seems Form is just a List under the hood. So by applying .scrollContentBackground(.hidden) you are clearing the List background.
By using .background on Form you are setting the background for the entire List again. To set the background only on the implicit Section you need another modifier. .listRowBackground.
But listRowBackground has this signature:
func listRowBackground<V>(_ view: V?) -> some View where V : View
So you canĀ“t use .thinMaterial. But you can add a new background to the VStack.
A possible solution would be:
var body: some View {
NavigationStack(path: $path) {
ZStack {
LinearGradient(gradient: Gradient(colors: [.pink, .yellow]),
startPoint: .topTrailing,
endPoint: .bottomLeading)
.edgesIgnoringSafeArea(.all)
Form {
VStack {
TextField("", text: $text)
Button("test"){
}
.buttonStyle(.borderedProminent)
Button("test"){
}.buttonStyle(.borderedProminent)
}
// this will clear the background
.listRowBackground(Color.clear)
// add some padding around the VStack
.padding()
// apply a new background
.background(.ultraThinMaterial)
// make the edges round again
.mask {
RoundedRectangle(cornerRadius: 20)
}
}
.scrollContentBackground(.hidden)
}
}
}
Result:
Change the form's opacity to bleed through the background colour/image:
Form {
/...
}
.opacity(0.5). // 0 = fully translucent ... 1 = opaque

Tab Bar Customisation (tab bar item positioning) with Tabview in SwiftUI

I am trying to figure out how to use the functions from xcode with very limited information from the Apple documentation. How to use certain functions or variables or where to find examples and detailed explanations? The particular case that I am working on now is to offset the tab bar item images on the tab bar. I came across "itemPositioning", "stackedItemPositioning" but I have no clues about how to use them. Should I put it inside the initialiser function? And how to use it?
Also, a quick question. Is there any book or video covering like somethings that teach me how to explore unknown things on SwiftUI independently?
truct MainContentView: View {
#State private var navSelection = 0
init(){
UITabBar.appearance().backgroundColor = UIColor.clear
//stackedItemPositioning = UIOffset(horizontal: 0, vertical: -10)
}
var body: some View {
VStack(alignment: .center){
NavigationView{
TabView(selection: self.$navSelection){
Text("hey")
.tabItem{
Image(systemName: "heart.fill")
.offset(y:-15)
}
.tag(0)
Text("hey")
.tabItem{
Image(systemName: "magnifyingglass")
}
.tag(1)
Text("hey")
.tabItem{
Image(systemName: "capsule.portrait.fill")
}
.tag(2)
Text("hey")
.tabItem{
Image(systemName: "wand.and.stars")
}
.tag(3)
}
}
}
}
}

Adding a ZStack around a TabView/NavigationView moves content up into safe area when .blur is used

My application uses a TabView and contained in it there is a NavigationView so I can transition to different views on the selected tabs. I am using .navigationTitle together with .navigationBarTitleDisplayMode(.large) to show users where they are (in the example below the title is set to "Pfizer"). All is working fine as you can see below:
var body: some View {
TabView(selection: $selectedTab) {
NavigationView {
...
However, as soon as I put a ZStack around the TabView (or use .overlay) the entire views including the navigation header move up into the safe area even though I have not specified an .ignoresSafeArea anywhere! Here is how it looks like when I am using ZStack around it:
var body: some View {
ZStack(alignment: .center) {
TabView(selection: $selectedTab) {
NavigationView {
...
Note that I intend to use ZStack to conditionally show a view on top of my TabView/NavigationView as follows:
#Binding var condition: Bool
var body: some View {
ZStack(alignment: .center) {
TabView(selection: $selectedTab) {
NavigationView {
...
}
}
.blur(radius: condition ? 3 : 0)
if condition {
VStack {
VStack(alignment: .center) {
Text("Some text")
.font(.title2).padding(.bottom)
}
.padding()
.background(Color(.secondarySystemBackground))
}
.padding(.horizontal, 40)
}
}
}
I managed to figure out by trial and error that the problem only occurs if I am using the .blur view modifier on the TabView that is inside the ZStack. If I remove the .blur view modifier, then the problem that I described does not occur and so the initial comment of Asperi makes sense.
I do not understand this behavior and I have no idea why using .blur messes up the safe area. Can anybody explain this behavior to me please?

SwiftUI, how to change View (don't use button) color when pressing?

As the title, it's a typical use case. And it's easy to achieve if using SwiftUI buttonStyle.
But for some reason I cannot use Button. So, how to do it with View?
You can use onTapGesture modifier for your view
struct ContentView: View {
#State var color = Color.green
var body: some View {
Text("Hello")
.padding()
.background(color)
.onTapGesture {
color = Color.blue
}
}
}

SwiftUI NavigationView with transparent background

I Have this view with a Gradient as a background, I want to place another view on top of it, this last view has a NavigationView inside but for some reason I can't make it have a transparent background so I can show off the gradient behind.
Even more strange is the fact that is not even possible to change the NavigationView's background color, I ve looked every where but it seems that I can't find any method that allows me to change color nor to make it transparent
First View( The one with the gradient background I want to display)
ZStack { // Global Stack for views
//Background gradient
VStack {
LinearGradient(gradient: Gradient(colors: [Color("background2"), Color(.systemBackground)]), startPoint: .top, endPoint: .bottom)
.frame(height: screenHeight/4)
Spacer()
}.background(Color(.systemBackground))
Subjects()
VStack {
HStack { // Topbar with menu-btn and profile-btn
MenuButton(show: self.$showMenu)
.disabled(self.showProfile)
Spacer()
HStack {
TodayButton(show: self.$showToday)
ProfileButton(show: self.$showProfile)
}
}
Spacer()
}
.padding()
.padding(.top, screenHeight*0.05)
}
Second View (The one with the NavigationView I want to make transparent)
struct Subjects: View {
let subjects = [
Subject(id: UUID(), name: "Matematica", color: "ff06f0", grades: [3,7,6.5,5.5]),
Subject(id: UUID(), name: "Informatica", color: "5506f9", grades: [7,5,4.5,6,9]),
Subject(id: UUID(), name: "Geografia", color: "f39904", grades: [2,5,10,6.5,9,10,4.5])
]
var body: some View {
ZStack(alignment: .bottomTrailing) {
ZStack {
NavigationView {
VStack(spacing: 5) {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 30) {
ForEach(subjects) { subject in
SubjectCard(subject: subject)
}
}.padding()
.padding(.top)
}
}.navigationBarTitle(Text("Materie"))
}
}.offset(y: screenHeight*0.1)
ActionButton(icon: "plus")
}
}
}
I had almost the exact same problem on a NavigationView containing Lists and Navigation Links. These appeared white, blocking my gradient background, which was underneath it on the ZStack. I never found an answer that worked, but I did find a workaround.
I solved the problem by adding .blendMode(.darken) on a VStack containing these elements. Darken blendmode will pick the darker of the two views and display it. If your gradient is lighter, you may want to try .blendMode(.lighten). See my code below to see if it would work for you.
NavigationView {
ZStack {
LinearGradient(gradient: Gradient(colors: [Color.purple , Color.green]), startPoint: .topLeading, endPoint: .bottomTrailing)
.ignoresSafeArea()
VStack {
SideMenuHeaderView()
VStack {
List(SideMenuViewModel.MenuItem.allCases) { itemText in
NavigationLink(destination: viewModel.getDestination(itemText: itemText.rawValue)) {
SideMenuCell(text: itemText.rawValue)
}
}
}.blendMode(.darken)
.padding()
}
}
}
Hopefully that works for you as well.

Resources