Dynamically change toolbarColorScheme SwiftUI - swiftui

How can you change the color of the status bar dynamically in SwiftUI? I can set it initially, but I'd like to have it dynamic.
.toolbarColorScheme(statusColor, for: .navigationBar)
In a perfect world, this is what I would like to do:
#State private var statusColor:ColorScheme = .dark
func body(content: Content) -> some View {
content
.frame(maxWidth: .infinity, maxHeight: .infinity)
.toolbarColorScheme($statusColor, for: .navigationBar)
}
And have the ability to change the value, for example:
func makeLight() {
statusColor = .light
}
func makeDark() {
statusColor = .dark
}
This doesn't seem possible. I've tried it multiple ways. The example above gives the error:
Cannot convert value of type 'Binding' to expected
argument type 'ColorScheme?'

try this approach, works for me:
struct ContentView: View {
#State private var statusColor: ColorScheme = .dark
var body: some View {
NavigationStack {
Button("change color scheme") {
statusColor = statusColor == .dark ? .light : .dark
}
Text("main view")
.toolbar {
ToolbarItem {
Button("change scheme") {
statusColor = statusColor == .dark ? .light : .dark
}
}
}
.navigationBarTitle("test", displayMode: .inline)
.toolbarBackground(.visible, for: .navigationBar)
.toolbarColorScheme(statusColor, for: .navigationBar)
}
}
}

Related

FocusState Textfield not working within toolbar ToolbarItem

Let me explain, I have a parent view with a SearchBarView, im passing down a focus state binding like this .
SearchBarView(searchText:$object.searchQuery, searching: $object.searching, focused: _searchIsFocused
That works perfectly as #FocusState var searchIsFocused: Bool is defined in parent view passing it down to the SearchBarView (child view ). In parent I can check the change in value and everything ok.
The problem relies when in parent I have the SearchBarView inside .toolbar {} and ToolBarItem(). nothing happens, not change in value of focus, etc. I have my SearchBarView in the top navigation bar and still want to use it there.. but I need to be able to know when it is in focus. if I use inside any VStack or whatever, everything perfectly..
-- EDIT --
providing more code to test
SearchBarView
struct SearchBarView: View {
#Environment(\.colorScheme) var colorScheme
#Binding var searchText: String
#Binding var searching: Bool
#FocusState var focused: Bool
var body: some View {
ZStack {
Rectangle()
.foregroundColor(colorScheme == .dark ? Color("darkSearchColor") : Color.white)
.overlay(
RoundedRectangle(cornerRadius: 13)
.stroke(.black.opacity(0.25), lineWidth: 1)
)
HStack {
Image(systemName: "magnifyingglass").foregroundColor( colorScheme == .dark ? .gray : .gray )
TextField("Search..", text: $searchText )
.focused($focused, equals: true)
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 20))
.disableAutocorrection(true).onSubmit {
let _ = print("Search textfield Submited by return button")
}
}
.foregroundColor(colorScheme == .dark ? Color("defaultGray") :.gray)
.padding(.leading, 13)
.padding(.trailing, 20).overlay(
HStack {
Spacer()
if searching {
ActivityIndicator().frame(width:15,height:15).aspectRatio(contentMode: .fit).padding(.trailing,15)
}
}
)
.onChange(of: focused) { searchIsFocused in
let _ = print("SEARCH IS FOCUSED VALUE: \(searchIsFocused) ")
}
}
.frame(height: 36)
.cornerRadius(13)
}
}
-- home View Code --
struct HomeView: View {
#Environment(\.colorScheme) var colorScheme
#FocusState var searchIsFocused: Bool
#State var searching:Bool = false
#State var searchQuery: String = ""
var body: some View {
NavigationStack {
GeometryReader { geofull in
ZStack(alignment: .bottom) {
Color("background")//.edgesIgnoringSafeArea([.all])
ScrollView(showsIndicators: false) {
VStack {
// Testing Bar inside VStack.. Here It Works. comment the bar the leave
// the one inside the .toolbar ToolbarItem to test
SearchBarView(searchText:$searchQuery, searching: $searching, focused: _searchIsFocused).padding(0)
}.toolbar {
//MARK: Navbar search field
ToolbarItem(placement:.principal) {
SearchBarView(searchText:$searchQuery, searching: $searching, focused: _searchIsFocused).padding(0)
}
}
.onChange(of: searchIsFocused) { searchIsFocused in
let _ = print("HOME VIEW searchIsFocused VALUE: \(searchIsFocused) ")
}
}
}
}
}
}
}

SwiftUI: Changing Light/Dark Mode in Sheet Does Not Refresh Sheet UI

I want to have a system/light/dark mode picker in a sheet, along with other settings.
Selecting a different scheme does correctly change the UI in the ContentView (behind the sheet), but the sheet itself retains the old scheme and must be dismissed and opened again.
What am I doing wrong?
Example:
import SwiftUI
struct ContentView: View {
#EnvironmentObject var viewManager: ViewManager
#State var showSettingsSheet = false
var body: some View {
NavigationView {
List {
Text("Some content 1")
Text("Some content 2")
}
.toolbar {
ToolbarItem {
Button(action: {
showSettingsSheet.toggle()
}){
Image(systemName: "gearshape.fill")
}
.sheet(isPresented: $showSettingsSheet){SettingsSheet()}
}
}
}
.preferredColorScheme(viewManager.colorScheme == "Light" ? .light : viewManager.colorScheme == "Dark" ? .dark : nil)
}
}
struct SettingsSheet: View {
#Environment (\.presentationMode) var presentationMode
#EnvironmentObject var viewManager: ViewManager
var body: some View {
NavigationView{
GroupBox {
Picker("Display Mode", selection: $viewManager.colorScheme) {
Text("System").tag("System")
Text("Light").tag("Light")
Text("Dark").tag("Dark")
}
.pickerStyle(.segmented)
}
.toolbar {
ToolbarItem {
Button(action: {
presentationMode.wrappedValue.dismiss()
}){
Text("Done")
}
}
}
}
}
}
class ViewManager: ObservableObject {
#AppStorage("colorScheme") var colorScheme: String = "System"
}
You have to provide colorScheme to sheet manually as it is outside your view hierarchy, i.e.:
struct ContentView: View {
#EnvironmentObject var viewManager: ViewManager
#State var showSettingsSheet = false
var body: some View {
NavigationView {
List {
Text("Some content 1")
Text("Some content 2")
}
.toolbar {
ToolbarItem {
Button(action: {
showSettingsSheet.toggle()
}){
Image(systemName: "gearshape.fill")
}
.sheet(isPresented: $showSettingsSheet){
SettingsSheet()
.preferredColorScheme(viewManager.colorScheme == "Light" ? .light : viewManager.colorScheme == "Dark" ? .dark : nil) // HERE
}
}
}
}
.preferredColorScheme(viewManager.colorScheme == "Light" ? .light : viewManager.colorScheme == "Dark" ? .dark : nil)
}
}

How can I have Image and Text for Picker in SegmentedPickerStyle in SwiftUI?

I am building a Picker with SwiftUI.
Now i want do add an icon AND text for each selection. So it should look something like this:
Is this possible? If yes how to do it?
Or is it not recommended by Apples apples human interface guidelines at all?
I already tried to use a HStack to wrap image and text together.
enum Category: String, CaseIterable, Identifiable {
case person
case more
var id: String { self.rawValue }
}
struct ContentView: View {
#State private var category = Category.person
var body: some View {
Picker("Category", selection: $category) {
HStack {
Image(systemName: "person")
Text("Person")
}.tag(Category.person)
HStack {
Image(systemName: "ellipsis.circle")
Text("More")
}.tag(Category.more)
}.pickerStyle(SegmentedPickerStyle())
.padding()
}
}
But the framework splits it up into four.
You can make a custom Picker
struct ContentView: View {
var body: some View {
Home()
}
}
struct Home: View {
#State var index = 0
var body: some View {
VStack {
HStack {
Text("Picker with icon")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.black)
Spacer(minLength: 0)
}
.padding(.horizontal)
HStack(spacing: 0){
HStack{
Image(systemName: "person")
.foregroundColor(self.index == 0 ? .black : .gray)
Text("Person")
.foregroundColor(self.index == 0 ? .black : .gray)
}
.padding(.vertical, 10)
.padding(.horizontal, 35)
.background((Color.white).opacity(self.index == 0 ? 1 : 0))
.clipShape(Capsule())
.onTapGesture {
self.index = 0
}
HStack{
Image(systemName: "ellipsis.circle")
.foregroundColor(self.index == 1 ? .black : .gray)
Text("More")
.foregroundColor(self.index == 1 ? .black : .gray)
}
.padding(.vertical, 10)
.padding(.horizontal, 35)
.background((Color.white).opacity(self.index == 1 ? 1 : 0))
.clipShape(Capsule())
.onTapGesture {
self.index = 1
}
}
.padding(3)
.background(Color.black.opacity(0.06))
.clipShape(Capsule())
Spacer(minLength: 0)
}
.padding(.top)
}
}
This is a way using Apple Picker with the output you want:
enum Category: String, CaseIterable, Identifiable {
case person
case more
var id: String { self.rawValue }
}
struct ContentView: View {
#State private var category = Category.person
private var view1: some View { HStack { Image(systemName: "person"); Text("Person") } }
private var view2: some View { HStack { Image(systemName: "ellipsis.circle"); Text("More") } }
#State private var uiImage1: UIImage? = nil
#State private var uiImage2: UIImage? = nil
var body: some View {
return Picker("Category", selection: $category) {
if let unwrappedUIImage1 = uiImage1 {
Image(uiImage: unwrappedUIImage1)
.tag(Category.person)
}
if let unwrappedUIImage2 = uiImage2 {
Image(uiImage: unwrappedUIImage2)
.tag(Category.more)
}
}
.pickerStyle(SegmentedPickerStyle())
.padding()
.onAppear() {
DispatchQueue.main.async {
uiImage1 = viewToUIImageConverter(content: view1)
uiImage2 = viewToUIImageConverter(content: view2)
}
print("Your selection is:", category.rawValue)
}
.onChange(of: category, perform: { newValue in print("Your selection is:", newValue.rawValue) })
}
}
func viewToUIImageConverter<Content: View>(content: Content) -> UIImage? {
let controller = UIHostingController(rootView: content)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = UIColor.clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}

Color Change Animation

I'm trying to animate a color change on some text but I can't seem to get it to change gradually. I've tried both an implicit and explicit animation as seen in the code below, but no dice....
struct Example: View {
#State var showing = false
var body: some View {
VStack {
Text("test text").foregroundColor(showing ? .red : .blue)
.animation(.easeIn(duration: 2))
Button(action: toggle) {
Text("Toggle")
}
}
}
func toggle() {
withAnimation(.easeIn(duration: 2)) {self.showing.toggle()}
}
}
Can anyone give me some pointers?
Unfortunately, you can't animate .foregroundColor. But you can animate .colorMultiply. So in your case this will work:
struct ColorChangeAnimation: View {
#State private var multiplyColor: Color = .blue
var body: some View {
VStack {
Text("test text")
.foregroundColor(.white)
.colorMultiply(multiplyColor)
Button(action: toggle) {
Text("Toggle")
}
}
}
func toggle() {
withAnimation(.easeIn(duration: 2)) {
self.multiplyColor = (self.multiplyColor == .red) ? .blue : .red
}
}
}

How do I change my view's background color using List (SwiftUI)

I want to let my cell looks not fill in list's column. I have already clear the list background color and
separatorStyle set .none. I also set my cellView's listRowBackground been gray, but it doesn't work well.The background color is still white in my cell. How do I clear my list's column background color? Please help. Thank you.
struct TeamListView: View {
#EnvironmentObject var userToken : UserToken
#State var teamResults : [TeamResult] = []
var body: some View {
NavigationView {
ZStack{
Color.gray.edgesIgnoringSafeArea(.all)
VStack {
List(teamResults) { team in
TeamListCellView(teamResult: team)
}.navigationBarTitle(Text("My team"),displayMode: .inline)
}
}
.onAppear(perform: {
self.getTeamData()
UITableView.appearance().backgroundColor = .gray
UITableView.appearance().separatorStyle = .none
})
.onDisappear(perform: {
UITableView.appearance().backgroundColor = .white
UITableView.appearance().separatorStyle = .singleLine
})
}
Below is my cellView, I set the .listRowBackground(Color.gray) in here.
struct TeamListCellView: View {
// #ObservedObject var teamResult: TeamResult
var teamResult: TeamResult
var body: some View {
NavigationLink(destination: TeamDetail(teamResult1: teamResult)) {
Image(uiImage: teamResult.teamImage)
.resizable()
.aspectRatio(contentMode: ContentMode.fill)
.frame(width:70, height: 70)
.cornerRadius(35)
VStack(alignment: .leading) {
Text(teamResult.groupName)
Text(teamResult.groupIntro)
.font(.subheadline)
.foregroundColor(Color.gray)
}
} .frame(width:200,height: 100)
.background(Color.green)
.cornerRadius(10)
.listRowBackground(Color.gray)
}
}
You can create a Background<Content: View> and use it to set the background colour of your view. To do it you can embed your views inside your Background View
For example:
struct ContentView: View {
#EnvironmentObject var userToken : UserToken
#State var teamResults : [TeamResult] = []
var body: some View {
Background{
NavigationView {
ZStack{
Color.gray.edgesIgnoringSafeArea(.all)
VStack {
List(teamResults) { team in
TeamListCellView(teamResult: team)
}
.navigationBarTitle(Text("My team"),displayMode: .inline)
}
}
.onAppear(perform: {
self.getTeamData()
UITableView.appearance().backgroundColor = .gray
UITableView.appearance().separatorStyle = .none
})
.onDisappear(perform: {
UITableView.appearance().backgroundColor = .white
UITableView.appearance().separatorStyle = .singleLine
})
}
}
}
}
struct Background<Content: View>: View {
private var content: Content
init(#ViewBuilder content: #escaping () -> Content) {
self.content = content()
}
var body: some View {
Color.gray
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.overlay(content)
}
}