I am very new at IOS dev, this is my day one. so I have been playing with SF symbols and tried to add it into a button, but somehow it doesn't appear at the button
The Bug
The color should be black and it should've appear on the button.
Perhaps I made a beginner mistake, but still don't know ho two fix it. so what did I do wrong?
struct ButtonBig: View {
var ButtonText:String
var ButtonIcon:String
var body: some View {
ZStack{
RoundedRectangle(cornerRadius: 25.0)
.frame(width:117, height: 112.36, alignment:.center)
.foregroundColor(.blue)
VStack{
Image(systemName: ButtonIcon).padding()
Text(ButtonText).foregroundColor(.white)
}
}
}
}
}
Button(action: ) {
ButtonBig(ButtonText: "Button3",ButtonIcon: "calendar")
}
Add button style to PlainButtonStyle()
Button(action: ) {
ButtonBig(ButtonText: "Button3",ButtonIcon: "calendar")
}.buttonStyle(PlainButtonStyle()) //<-- Here
Or you can set renderingMode mode to original.
// Other code
VStack{
Image(systemName: ButtonIcon).renderingMode(.original).padding() //<--- Here
Text(ButtonText).foregroundColor(.white)
}
// Other code
You can do it by overlaying your button image and text on the RoundedRectangle shape view like below :
struct ButtonBig: View {
var ButtonText:String
var ButtonIcon:String
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 25.0)
.frame(width:117, height: 112.36, alignment:.center)
.foregroundColor(.blue)
.overlay(
VStack{
Image(systemName: ButtonIcon).renderingMode(.original).padding()
Text(ButtonText).foregroundColor(.white)
}
)
}
}
}
Or you can also do it by just setting the button style directly to HStack of the button.
HStack {
Button(action: {
}, label: {
ButtonBig(ButtonText: "Button1",ButtonIcon: "calendar")
})
Button(action: {
}, label: {
ButtonBig(ButtonText: "Button2",ButtonIcon: "calendar")
})
Button(action: {
}, label: {
ButtonBig(ButtonText: "Button3",ButtonIcon: "calendar")
})
}.buttonStyle(PlainButtonStyle()) //Set the button style to HStack instead of each button
Related
I am trying to make a chat app, but having difficulties with the HStack. The border I have is going over the edge of the view so the full rounded rectangle isn't shown.
The code I have for the TextField and HStack is this.
var body: some View {
HStack {
CustomTextField(placeholder: Text("Enter your message"), text: $message)
Button {
messagesManager.sendMessage(message: message)
message = ""
} label: {
Image(systemName: "arrow.up.circle.fill")
.foregroundColor(.blue)
.padding(10)
.scaledToFit()
}
}
.padding(.horizontal)
.padding(.vertical, 0)
.border(.gray)
.cornerRadius(20)
}
struct CustomTextField: View {
var placeholder: Text
#Binding var text: String
var editingChanged: (Bool) -> () = {_ in}
var commit: () -> () = {}
var body: some View {
ZStack(alignment: .leading) {
if text.isEmpty {
placeholder
.opacity(0.5)
}
TextField("", text: $text, onEditingChanged: editingChanged, onCommit: commit)
}
}
}
I can't find out how to reduce the length of the HStack so the whole of the rounded rectangle will show, as well as increasing the size of the button to fill up more space within the shape.
You can use overlay with RoundedRectangle for the border with a corner radius. I have added leading padding to your CustomTextField so that it will look proper and horizontal padding to your HStack after adding overlay.
var body: some View {
HStack {
CustomTextField(placeholder: Text("Enter your message"), text: $message)
.padding(.leading, 10)
Button {
//messagesManager.sendMessage(message: message)
message = ""
} label: {
Image(systemName: "arrow.up.circle.fill")
.foregroundColor(.blue)
.padding(10)
.scaledToFit()
}
}
.overlay(
RoundedRectangle(cornerRadius: 20)
.stroke(.gray)
)
.padding(.horizontal)
}
Preview
I'm struggling with a view where I want to have multiple pickers embedded in
other views. When I wrap the pickers in a Form, I get the desired behavior for the
picker but there is a lot of extra space around the pickers that I can't seem to
automatically adjust.
This is an example - the space in the red outline seems to be determined by the other
view elements not the size of the picker.
I can, of course, hard-code a frame height for the Form but that is trial and error
and would only be specific to the device and orientation. I have tried multiple
versions of Stacks inside Stacks with padding, GeometryReader etc, but I have not come up with any
solution. As an aside, I DO want the picker labels, otherwise I could just remove
the Form.
I also tried setting UITableView.appearance().tableFooterView in an init() but that did not work either.
Here is a simplified version:
struct ContentView4: View {
#State var selectedNumber1: Int = 1
#State var selectedNumber2: Int = 2
#State var selectedNumber3: Int = 3
var body: some View {
NavigationView {
VStack(alignment: .leading) {
HStack {
Spacer()
Text("Compare up to 3")
.font(.caption)
Spacer()
}//h
Form {//for pickers
Picker(selection: $selectedNumber1, label: Text("A")) {
ForEach(0..<10) {
Text("\($0)")
}
}//picker
Picker(selection: $selectedNumber2, label: Text("B")) {
ForEach(0..<10) {
Text("\($0)")
}
}//picker
Picker(selection: $selectedNumber3, label: Text("C")) {
ForEach(0..<10) {
Text("\($0)")
}
}//picker
}//form for pickers
.padding(.horizontal, 10)
//.frame(height: 200) //don't want to hard code this
VStack(alignment: .leading) {
HStack {
Text("A")
.frame(width: 100)
Text("B")
.frame(width: 100)
Text("C")
.frame(width: 100)
}
.padding(.horizontal, 10)
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .leading){
Text("A title line")
.font(.headline)
.padding(.vertical, 5)
HStack {
Text("Number")
.frame(width: 100)
Text("Number")
.frame(width: 100)
Text("Number")
.frame(width: 100)
}
Text("Another title line")
.font(.headline)
.padding(.vertical, 5)
HStack {
Text("Something")
.frame(width: 100)
Text("Something")
.frame(width: 100)
Text("Something")
.frame(width: 100)
}
Text("A Third title line")
.font(.headline)
.padding(.vertical, 5)
HStack {
Text("More")
.frame(width: 100)
Text("More")
.frame(width: 100)
Text("More")
.frame(width: 100)
}
}
}//scroll
.padding(.horizontal, 10)
}
.navigationBarTitle("Compare Three", displayMode: .inline)
}
}//nav
}//body
}//struct
Interestingly, I am able to get a solution by removing the form and wrapping each
picker in a menu, like this:
Menu {
Picker(selection: $selectedNumber2, label: EmptyView()) {
ForEach(0..<10) {
Text("\($0)")
}
}//picker
} label: {
HStack {
Text("B")
Spacer()
Image(systemName: "chevron.right")
.resizable()
.frame(width: 14, height: 14)
}//h
}//menu label
However, I still like the look of the Form better if I could automatically configure
the space around the Form items.
Any guidance would be appreciated. Xcode 13.4, iOS 15.5
Form (and List) is not meant to be stacked inside other views like this, which is why it has such strange behavior.
Thankfully, it's fairly simple to recreate the stuff you do want using NavigationLink. Here’s a quick example of a couple custom views that do just that:
// drop-in NavigationLink replacement for Picker
struct NavigationButton<Content: View, SelectionValue: Hashable> : View {
#Binding var selection: SelectionValue
#ViewBuilder let content: () -> Content
#ViewBuilder let label: () -> Text
var body: some View {
NavigationLink {
PickerView(selection: $selection, content: content, label: label)
} label: {
HStack {
label()
Spacer()
Text(String(describing: selection))
.foregroundColor(.secondary)
}
.contentShape(Rectangle())
}
.buttonStyle(NavigationLinkButtonStyle())
}
}
// subview for the Picker page, which lets us use `dismiss()`
// to pop the subview when the user selects an option
struct PickerView<Content: View, SelectionValue: Hashable> : View {
#Binding var selection: SelectionValue
#ViewBuilder let content: () -> Content
#ViewBuilder let label: () -> Text
#Environment(\.dismiss) private var dismiss
var body: some View {
Form {
Picker(selection: $selection, content: content, label: label)
.pickerStyle(.inline)
.labelsHidden()
.onChange(of: selection) { _ in
dismiss()
}
}
.navigationTitle(label())
}
}
// recreate the appearance of a List row
struct NavigationLinkButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.label
.frame(maxWidth: .infinity)
Image(systemName: "chevron.right")
.font(.footnote.bold())
.foregroundColor(Color(UIColor.tertiaryLabel))
}
.padding()
.background(
Rectangle()
.fill(configuration.isPressed ? Color(UIColor.quaternaryLabel) : Color(UIColor.systemBackground))
)
}
}
If you like the .insetGrouped style you got using Form, we can replicate that by putting NavigationButton inside a clipped VStack:
VStack(spacing: 0) {
NavigationButton(selection: $selectedNumber1) {
ForEach(0..<10) {
Text("\($0)")
}
} label: {
Text("A")
}
Divider()
NavigationButton(selection: $selectedNumber2) {
ForEach(0..<10) {
Text("\($0)")
}
} label: {
Text("B")
}
}
.clipShape(RoundedRectangle(cornerRadius: 11))
.padding()
.background(Color(UIColor.systemGroupedBackground))
And here’s a screenshot showing my custom views above your original Form.
(And if you like Picker as a popup menu, you could use Menu instead of NavigationLink)
I am trying to create a profile page, in which there is a form at the bottom with various options. However when I insert a Spacer() into the VStack, the form does not move to the bottom of the screen, as it should do. I tried replacing the Form with a Text and it worked fine, moving to the bottom of the screen. So I'm assuming it has something to do with the form.
Here is my code
struct Profile: View {
#Environment(\.presentationMode) var mode: Binding<PresentationMode>
var body: some View {
NavigationView {
VStack {
Image(systemName: "person.crop.circle")
.resizable()
.frame(width: 50, height: 50)
// .padding(.top)
Text("email#email.com")
.font(.title)
Spacer()
Form {
Section {
//menuListItem(image: "gear", label: "Settings")
menuListItem(image: "questionmark.circle", label: "Help") menuListItem(image: "info.circle", label: "About")
}
Section {
HStack {
Spacer()
Button(action: {
UserDefaults.standard.set(false, forKey: "LoggedIn")
UserDefaults.standard.set(nil, forKey: "user_id")
UserDefaults.standard.set(nil, forKey: "school_id")
self.mode.wrappedValue.dismiss()
}) {
Text("Log Out")
.font(.body)
.foregroundColor(.red)
}
Spacer()
}
}
}
}
.navigationBarTitle("Profile", displayMode: .inline)
}
}
}
struct menuListItem: View {
var image: String
var label: String
var body: some View {
HStack {
Image(systemName: image)
Text(label)
.font(.body)
}
}
}
As long as bottom of the Form is unknown(because Form is scrollable and its items start from the top), swiftUI does not know how much space should put between those two views. So Spacer() does not help.
Try this:
Give your form: for example, .frame(height: 500) or .padding(.top, 400) modifier.
I have this code for my view
struct ContentView: View {
var body: some View {
NavigationView{
List{
ForEach(0...5, id: \.self) { note in
VStack(alignment: .leading) {
Text("title")
Text("subtitle")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
.navigationBarItems(trailing: resetButton)
.navigationBarTitle(Text("Notes"))
}
}
var resetButton: some View {
Button(action: {
print("reset")
}) {
Image(systemName: "arrow.clockwise")
}
.background(Color.yellow)
}
}
resetButton looks like this:
When I am tapping the resetButton, it seems like only the yellow area responds to touches.
How do I make tappable area of this button bigger? (Make it behave like a normal UIBarButtonItem)
You can change the frame of the view inside the button:
var resetButton: some View {
Button(action: {
print("reset")
}) {
Image(systemName: "arrow.clockwise")
.frame(width: 44, height: 44) // Or any other size you like
}
.background(Color.yellow)
}
This blog post pointed me in the right direction, I needed to add some padding directly to the Image.
Button(action: {
// Triggered code
}) {
Image(systemName: "plus")
.font(.system(size: 22, weight: .regular))
.padding(.vertical)
.padding(.leading, 60)
}
.background(Color.red) // Not needed
I'm struggling to remove the background of a custom circular Button element in SwiftUI which is defined as follows:
struct NavButton: View {
var body: some View {
Button(action: {})
VStack {
Text("Button")
}
.padding(40)
.background(Color.red)
.font(.title)
.mask(Circle())
}
}
}
This results in a rectangular light gray background around the button, where I want it to not be shown:
I tried to append a "background" modifier to the button, and it demonstrates very strange behavior: if it's set to "Color.clear", there is no effect. But if I set it to "Color.green" it does change the background as expected.
Example of setting the "Background" modifier to "Color.green":
struct NavButton: View {
var body: some View {
Button(action: {})
VStack {
Text("Button")
}
.padding(40)
.background(Color.red)
.font(.title)
.mask(Circle())
}
.background(Color.green) // has no effect if set to "Color.clear"
}
}
I wonder if I'm missing something here?
PS: I'm using Xcode 11.1 (11A1027)
Try adding .buttonStyle(PlainButtonStyle()) on the button itself.
You would have something like this:
Button(action: {}){
VStack{
Text("Button")
}
.padding(40)
.background(Color.red)
.font(.headline)
.mask(Circle())
}
.buttonStyle(PlainButtonStyle())
Declare your own ButtonStyle:
struct RedRoundButton: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(40)
.font(.title)
.background( Circle()
.fill(Color.red))
}
}
and then use it like this:
Button("Button") {}
.buttonStyle(RedRoundButton())
Try this:
struct ContentView: View {
var body: some View {
Button(action: {}) {
Text("Button")
.frame(width: 80, height: 80)
}
.background(Color.red)
.cornerRadius(40)
.frame(width: 80, height: 80)
}
}
Try this.
struct ContentView: View {
var body: some View {
VStack {
Button(action: {}){
Text("button")
.font(.system(size: 50))
.foregroundColor(.black)
.bold()
}
.padding(30)
.background(Color.yellow)
.font(.headline)
.mask(Circle())
.buttonStyle(PlainButtonStyle())
} // end Vstack
}// end body
}