In a macOS app, I set NSWindow.backgroundColor = .clear.
When SwiftUI views re-render, their previously rendered versions often get stuck in the background.
Is it a bug, or am I doing something wrong?
Do you know of a workaround?
Environment:
macOS 12.5.1
Xcode 14.0
Thank you in advance.
import SwiftUI
#main
struct MacosPlaygroundApp: App {
var body: some Scene {
WindowGroup("Playground") {
ContentView()
.frame(width: 120, height: 300, alignment: .center)
}
}
}
struct ContentView: View {
private let items: [Item] = [
Item(id: "first", name: "First", sfSymbol: "house"),
Item(id: "second", name: "Second", sfSymbol: "sun.max"),
Item(id: "third", name: "Third", sfSymbol: "cloud"),
Item(id: "fourth", name: "Fourth", sfSymbol: "trash"),
Item(id: "fifth", name: "Fifth", sfSymbol: "scribble"),
]
var body: some View {
ScrollView {
LazyVGrid(columns: [gridItem], alignment: .center, spacing: 10) {
ForEach(items, id: \.id) { item in
itemView(item: item)
}
}
}
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
if let mainWindow = NSApplication.shared.windows.first(where: { $0.title == "Playground" }) {
mainWindow.backgroundColor = .clear
}
}
}
#ViewBuilder
private func itemView(item: Item) -> some View {
VStack(alignment: .center, spacing: 10) {
Image(systemName: item.sfSymbol)
Text(item.name)
}
.padding()
.background(.pink.opacity(0.5))
.cornerRadius(10)
}
private var gridItem: GridItem {
GridItem(
.adaptive(minimum: 100, maximum: 100),
spacing: 10,
alignment: .top
)
}
private struct Item {
let id: String
let name: String
let sfSymbol: String
}
}
I found a workaround:
.backgroundColor = .white.withAlphaComponent(0.000001), instead of .clear.
The window still appears transparent (enough), and I cannot see the rendering bug happen.
Related
I'm making a MacOS app and a list of Mission with its own toolbar, and when clicking on a Mission, some informations about it are displayed with also its own toolbar.
The thing is that sometimes the border is here, sometimes not depending on the size of my Mission list -> here with border and here with no border.
Here is a sample of my code :
Entry point
ZStack {
NavigationLink(
isActive: Binding(
get: { selectedItem == nil },
set: { _ in }),
destination: { NoSelectionView() },
label: { EmptyView() })
List {
ForEach(missionsClients) { missionClient in
NavigationLink(), tag: missionClient.id, selection: $selectedItem, label: { MissionRow() })
}
}
}
.navigationTitle("Missions")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: { createNewMission = true },
label: { Image(systemName: "plus") })
}
}
NoSelectionView
struct NoSelectionView: View {
var body: some View {
Text("No Selection")
.foregroundColor(.gray)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.background()
.ignoresSafeArea()
.toolbar {
ToolbarItem(placement: .destructiveAction) {
Button(action: { }, label: { Image(systemName: "trash") }).disabled(true)
}
ToolbarItem(placement: .primaryAction) {
Button(action: { }, label: { Image(systemName: "square.and.pencil") }).disabled(true)
}
}
}
}
MissionRow
struct MissionRow: View {
var missionClient: MissionClient
var body: some View {
VStack(alignment: .leading) {
if let name = missionClient.mission.name.presence {
Text(name)
} else {
NoNameText()
}
if let clientName = missionClient.client.name.presence {
Text(clientName).foregroundStyle(.secondary)
}
}
}
}
Is there a way to enable Gradient Buttons [ + | – ] with SwiftUI? Can't find any useful information about this topic.
https://developer.apple.com/design/human-interface-guidelines/components/menus-and-actions/buttons#gradient-buttons/
Update
I made a few cosmetic changes to #workingdogsupportUkraine answer. Now Gradient Buttons looks similar as in the Settings app on macOS 13. Please feel free to suggest any enhancements
import SwiftUI
struct TestView: View {
#State private var selection: Int?
struct GradientButton: View {
var glyph: String
var body: some View {
ZStack {
Image(systemName: glyph)
.fontWeight(.medium)
Color.clear
.frame(width: 24, height: 24)
}
}
}
var body: some View {
Form {
Section {
List(selection: $selection) {
ForEach(0 ..< 5) { Text("Item \($0)") }
}
.padding(.bottom, 24)
.overlay(alignment: .bottom, content: {
VStack(alignment: .leading, spacing: 0) {
Divider()
HStack(spacing: 0) {
Button(action: {}) {
GradientButton(glyph: "plus")
}
Divider().frame(height: 16)
Button(action: {}) {
GradientButton(glyph: "minus")
}
.disabled(selection == nil ? true : false)
}
.buttonStyle(.borderless)
}
.background(Rectangle().opacity(0.04))
})
}
}
.formStyle(.grouped)
}
}
you could try something simple like this:
struct ContentView: View {
var body: some View {
List {
ForEach(0 ..< 5) { Text("item \($0)") }
HStack {
Button(action: {print("plus")}) {
Image(systemName: "plus").imageScale(.small)
}
Divider()
Button(action: {print("minus")}) {
Image(systemName: "minus").imageScale(.small)
}
}.buttonStyle(.borderless)
.frame(width: 66, height: 33)
}
}
}
I have tried to implement a search bar within my app (using swiftUI), which shows up when I build the app. However, it won't allow me to type into the actual text field. My code is below. I have been searching for solutions to this problem for awhile and can't seem to see any problems in my code. Is there something wrong with my TextField?
Code for search bar -
import SwiftUI
struct SearchBar: View {
#Binding var text: String
#State private var isEditing = false
var body: some View {
HStack {
TextField("Search...", text: $text)
// .foregroundColor(Color("Teal"))
// .background(Color("Grey"))
.overlay(
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(Color(UIColor.systemGray3))
.frame(minWidth:0, maxWidth: .infinity, alignment: .leading)
.padding(EdgeInsets.init(top: 0, leading: 30, bottom: 0, trailing: 20))
// Search icon
if isEditing {
Button(action: {
self.text = ""
}, label: {
Image(systemName: "multiply.circle")
.foregroundColor(Color(UIColor.systemGray3))
.padding(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 30))
// Delete button
})
}
}
).onTapGesture {
self.isEditing = true
}
if isEditing{
Button(action: {
self.isEditing = false
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
// Hide delete button when search bar is not selected
}){
}
}
}
}
}
Code implementing search bar into my view -
import SwiftUI
struct BusinessList: View {
#EnvironmentObject var modelData: ModelData
#State private var showFavoritesOnly = false
#State var searchText = ""
var filteredBusinesses: [Business] {
modelData.businesses.filter { business in
(!showFavoritesOnly || business.isFavorite)
}
}
var body: some View {
NavigationView {
ScrollView {
LazyVStack {
VStack {
SearchBar(text: $searchText)
.padding(.bottom)
.padding(.top)
.padding(.top)
.padding(.top)
.padding(.top)
// Search bar won't show text when 'typed into'
ScrollView(.horizontal, showsIndicators: false) {
HStack {
Button(action: {
showFavoritesOnly = true
}) {
Text("Favourites")
.font(.caption)
.fontWeight(.medium)
.foregroundColor(Color.white)
.frame(width: 100.0, height: 30)
.background(Color("Teal"))
// How the button looks
.cornerRadius(25)
.zIndex(1)
.textCase(.uppercase)
.padding(EdgeInsets.init(top: 0, leading: 20, bottom: 0, trailing: 0))
}
Button(action: {
}) {
Text("Hospitality")
.font(.caption)
.fontWeight(.medium)
.foregroundColor(Color.white)
.frame(width: 100.0, height: 30)
.background(Color("Teal"))
// How the button looks
.cornerRadius(25)
.zIndex(1)
.textCase(.uppercase)
}
Button(action: {
}) {
Text("Retail")
.font(.caption)
.fontWeight(.medium)
.foregroundColor(Color.white)
.frame(width: 100.0, height: 30)
.background(Color("Teal"))
// How the button looks
.cornerRadius(25)
.zIndex(1)
.textCase(.uppercase)
}
Button(action: {
}) {
Text("Lifestyle")
.font(.caption)
.fontWeight(.medium)
.foregroundColor(Color.white)
.frame(width: 110.0, height: 30)
.background(Color("Teal"))
// How the button looks
.cornerRadius(25)
.zIndex(1)
.textCase(.uppercase)
}
}
.padding(.bottom)
}
}
.background(Color(UIColor.white))
.shadow(radius: 6)
.padding(.bottom)
ForEach(filteredBusinesses) { business in
NavigationLink(destination: BusinessDetail(business: business)) {
BusinessRow(business: business)
}
}
}
}
.navigationBarHidden(true)
.ignoresSafeArea()
}
}
}
struct BusinessList_Previews: PreviewProvider {
static var previews: some View {
BusinessList()
}
}
your code works for me with minor changes to the colors, nothing major. Maybe the colors did not show your typing, but the text was there.
This is the test I did:
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct SearchBar: View {
#Binding var text: String
#State private var isEditing = false
var body: some View {
HStack {
TextField("Search...", text: $text)
// .foregroundColor(Color("Teal"))
// .background(Color("Grey"))
.overlay(
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.gray)
.frame(minWidth:0, maxWidth: .infinity, alignment: .leading)
.padding(EdgeInsets.init(top: 0, leading: 30, bottom: 0, trailing: 20))
// Search icon
if isEditing {
Button(action: {
self.text = ""
}, label: {
Image(systemName: "multiply.circle")
.foregroundColor(.gray)
.padding(EdgeInsets.init(top: 0, leading: 0, bottom: 0, trailing: 30))
// Delete button
})
}
}
).onTapGesture {
self.isEditing = true
}
if isEditing{
Button(action: {
self.isEditing = false
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
// Hide delete button when search bar is not selected
}){
}
}
}
}
}
struct ContentView: View {
#State var searchText = ""
var body: some View {
SearchBar(text: $searchText)
.frame(width: 222, height: 55).padding(.horizontal, 20)
.border(.red)
}
}
EDIT:
Your new code works for me, when you remove
.shadow(radius: 6)
and
.ignoresSafeArea()
I am working on a task manager app. Recently trying to edit an item inside the scroll view. While trying to do so my views just jammed and I can not see my scroll view on the view. Instead of scroll view my add NewTaskView appears.
You may see the whole project in https://github.com/m3rtkoksal/TaskManager
struct TaskListView: View {
#State private(set) var data = ""
#State var isSettings: Bool = false
#State var isSaved: Bool = false
#State var shown: Bool = false
#State var selectedTask = TaskElement(dateFrom: "", dateTo: "", title: "", text: "")
var body: some View {
NavigationView {
ZStack {
Color(#colorLiteral(red: 0.9333333333, green: 0.9450980392, blue: 0.9882352941, alpha: 1)).edgesIgnoringSafeArea(.all)
VStack {
TopBar()
HStack {
CustomTextField(data: $data, tFtext: "Find task", tFImage: "magnifyingglass")
Button(action: {
self.isSettings.toggle()
}, label: {
ZStack {
RoundedRectangle(cornerRadius: 15)
.frame(width: 50, height: 50, alignment: .center)
.foregroundColor(Color(#colorLiteral(red: 0.4274509804, green: 0.2196078431, blue: 1, alpha: 1)))
Image("buttonImage")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}
.padding(.horizontal, 15)
})
}
CustomSegmentedView()
ZStack {
TaskFrameView()
VStack {
Spacer()
HStack {
Spacer()
Button( action: {
self.isSaved.toggle()
}, label: {
ZStack {
RoundedRectangle(cornerRadius: 25)
.foregroundColor(Color(#colorLiteral(red: 1, green: 0.7137254902, blue: 0.2196078431, alpha: 1)))
Text("+")
.foregroundColor(.white)
.font(.title)
.fontWeight(.bold)
}
.frame(width: 50, height: 50)
})
}
}
NavigationLink(
destination: NewTaskView(isShown: $shown, task: selectedTask),
isActive: $shown,
label: {
Text("")
})
}
}
}
.navigationBarHidden(true)
Spacer()
}
.navigationBarHidden(true)
}
}
The first image is my mixed-up view
Second is NewTaskView and the last one is proper TaskListView
While this code works like a charm without editable scroll view
struct TaskListView: View {
#State private(set) var data = ""
#State var isSettings: Bool = false
#State var isSaved: Bool = false
var body: some View {
NavigationView {
ZStack {
Color(#colorLiteral(red: 0.9333333333, green: 0.9450980392, blue: 0.9882352941, alpha: 1)).edgesIgnoringSafeArea(.all)
VStack {
TopBar()
HStack {
CustomTextField(data: $data, tFtext: "Find task", tFImage: "magnifyingglass")
Button(action: {
self.isSettings.toggle()
}, label: {
ZStack {
RoundedRectangle(cornerRadius: 15)
.frame(width: 50, height: 50, alignment: .center)
.foregroundColor(Color(#colorLiteral(red: 0.4274509804, green: 0.2196078431, blue: 1, alpha: 1)))
Image("buttonImage")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}
.padding(.horizontal, 15)
})
}
CustomSegmentedView()
ZStack {
TaskFrameView()
VStack {
Spacer()
HStack {
Spacer()
Button( action: {
self.isSaved.toggle()
}, label: {
ZStack {
RoundedRectangle(cornerRadius: 25)
.foregroundColor(Color(#colorLiteral(red: 1, green: 0.7137254902, blue: 0.2196078431, alpha: 1)))
Text("+")
.foregroundColor(.white)
.font(.title)
.fontWeight(.bold)
}
.frame(width: 50, height: 50)
})
}
}
NavigationLink(
destination: NewTaskView(),
isActive: $isSaved,
label: {
Text("")
})
}
}
}
.navigationBarHidden(true)
Spacer()
}
.navigationBarHidden(true)
}
}
Because that is what you told it to show in your struct ScrollViewTask. You have a ZStack that contains a ScrollView that contains a VStack with a ForEach that is supposed to display your TaskElementViews(while not part of the answer, why not just use a List) as well as your NewTaskView. In essence, you would always have a NewTaskView displayed on top of any TaskElements you have. I suspect that your code is simply not actually displaying TaskElements, either due to a lack of data, or other incorrect coding. Regardless, your NewTaskView would always overlay it.
Below is a folded version of your code that makes it clear:
struct ScrollViewTask: View {
#ObservedObject private var obser = observer()
#State var selectedTask = TaskElement(dateFrom: "", dateTo: "", title: "", text: "")
#State var shown: Bool = false
var body: some View {
ZStack {
ScrollView(.vertical) {...}
.onAppear {...}
NewTaskView(isShown: $shown, task: selectedTask)
}
}
struct ScrollViewTask: View {
#ObservedObject private var obser = observer()
#State var selectedTask = TaskElement(dateFrom: "", dateTo: "", title: "", text: "")
#State var shown: Bool = false
var body: some View {
ScrollView(.vertical) {
VStack {
ForEach(self.obser.tasks) { task in
TaskElementView(task:task)
.onTapGesture {
self.selectedTask = task
self.shown.toggle()
}
}
}
}
.onAppear {
self.obser.fetchData()
}
.sheet(isPresented: $shown, content: {
NewTaskView(isShown: $shown, task: selectedTask)
})
}
}
I'd like to change default MenuButton style. I want to use the default button color instead of Color.gray
MenuButton("Actions") {
Button(action: {
}, label: { Text("Action 1") })
Button(action: {
}, label: { Text("Action 2") })
}
.menuButtonStyle(BorderlessButtonMenuButtonStyle())
.background(RoundedRectangle(cornerRadius: 5).foregroundColor(Color.gray))
I try to change the default style that looks like
to the style like button
I am using it like this:
.foregroundColor(.accentColor)
.background(Color(UIColor.systemBackground))
Created Style like this:
struct MainButtonStyle: PrimitiveButtonStyle {
let minWidth: CGFloat
let minHeight: CGFloat
func makeBody(configuration: Self.Configuration) -> some View {
Button(configuration)
.frame(minWidth: minWidth, minHeight: minHeight)
.font(.body)
.foregroundColor(.accentColor)
.background(Color(UIColor.systemBackground))
.buttonStyle(BorderlessButtonStyle())
.cornerRadius(5)
}
}
Use case:
Button(action: { }, label: {
Image(systemName: "map")
})
.buttonStyle(MainButtonStyle(minWidth: 30, minHeight: 30))