Background Img not staying on Screen Preview (SwiftUI w/ screen shots) - ios

Xcode Version - 13.1
I'm having some issues with a Background Img & the grouping Form{}. What's happening is when placing a background image in a ZStack and include the grouping option, Form{}, the Background Img disappears.
Below my code showing my Background Image inside a ZStack. Also including a link to a screen shot of the Preview -> https://i.stack.imgur.com/u12Sw.png
var body: some View {
ZStack {
Image("login")
.resizable()
.scaledToFill()
.edgesIgnoringSafeArea(.all)
}
}
However, when I add a Form{} inside the ZStack, the background image completely disappears and only the Form{} (w/ a TextField &SecureTextField) appears on the Preview Sans the Background Img. Below is the code w/ and a link to the Screen shot of the Preview -> https://i.stack.imgur.com/BI3U2.png
var body: some View {
ZStack {
Image("login")
.resizable()
.scaledToFill()
.edgesIgnoringSafeArea(.all)
Form {
TextField(
"Username (email)",
text: self.$username)
.autocapitalization(.none)
.disableAutocorrection(true)
SecureTextField(text: $password)
}
}
}
My assumption is that as long as the Form is inside the ZStack, the form should overlay the Background Img.
I like how the Form looks rather than two separate TextFields. Are there anyways to do this w/ the Form or something similar to a Form?

This is what I do at the moment:
I either add this modifier to the form.
.onAppear {
UITableView.appearance().backgroundColor = .clear
}
Or in an init() of the View
this has some side effects on the rest of the view if you have List or Forms but it works for me.

Related

Image at top of view extending under navigationbar

I am trying to make a "reusable" template for views in my app. As part of this I started prototyping this:
var body: some View {
NavigationView {
ZStack {
VStack {
// Spacer()
Image("progress_bar")
.resizable()
.scaledToFit()
.foregroundColor(Color.gray)
.background(Color.green)
HStack{
}
Spacer()
}
VStack{
}
}
}
.navigationBarHidden(true)
}
}
The ZStack contains 2 VStack. First one is my template and will be part of multiple of my screens later on. Second Stack is destined to be replaced by #ViewBuilder parameter so that I can reuse that in multiple screens easily.
The progress_bar image is a SVG file imported into assets, preserving Vector Data and rendered as template (So I can change colour).
My issue, as shown on the following screenshot, is that the image somehow extends toward the top of the screen. The green area correspond to the green coloured background added to the image. The progress bar is the grey line across the screen.
progress bar extending toward top of the screen
If I change my code to (commented out the spacer):
// Spacer()
Image("progress_bar")
.resizable()
.scaledToFit()
.foregroundColor(Color.gray)
.background(Color.green)
HStack{
}
Spacer()
}
I get this, progress bar shifts down in the screen (not wanted but expected) but the green area that was added on top of the image disappears:
updated screen with progress_bar shifted down and not over extending
I did try setting up a maxHeight to my Image view but this did not work out.
What am I missing? Is there a way I can stop this from happening?
Edit:
After more looking around, my issue is coming from the fact that the whole thing is embedded in a NavigationView. Apparently space is saved for the navigation bar even though it is hidden.

SwiftUI: Button with image got gray border on real iPhone but works fine on simulation mode

I'm trying to make simple app, the app seems working well on simulation mode, but test on my iPhone the button looks different from simulation iPhone,
link the simulation picture here,
enter image description here
this is the what I see on the simulation but real iPhone looks
enter image description here
please, check the pictures linked,
The button image made on sub View struct and called as a Button label,
plus I tried borderlessbuttonstyle, back and foreground color match both super, and child view. I got nothing, here the code,
I don't know how to delete the gray border not only these buttons but also all buttons that I used with image labeling looks the same way - bordered with gray rectangle,
here the button code
struct MainList: View {
#EnvironmentObject var pageControl: PageControl
var body: some View {
VStack{
Button{
pageControl.pageNum = 1
pageControl.detailPage = 0
print("button pressed pageNum: \(pageControl.pageNum), startPage \(pageControl.detailPage)")
} label: {
mainListRow(actuator: actuators[0])
.background(.white)
}
.buttonStyle(BorderlessButtonStyle())
.foregroundColor(.white)
and this is the View that used to button label
struct mainListRow: View {
var actuator: Actuator
var body: some View {
HStack(alignment: .center) {
Image("home")
Spacer()
VStack {
ZStack{
RoundedRectangle(cornerRadius: 10.0)
.frame(width: 60, height: 30)
.foregroundColor(.red)
Text(String(actuator.speed) + " %")
.foregroundColor(.white)
}
}
}
}
}
here add the another picture when I change the background color
enter image description here
as you can see that those three buttons, for the top button without background color, the button area is filled gray color, if I add background color - second button, it only filled inside, so I changed the background color to button field with black then the border color changed. thus I add the background color with white at button field not working - third button :(. the tested phone is iPhone Xs. this is really weird as I mentioned it work fine on simulation. please. test it on real iPhone.
here the code for third picture,
Button{
pageControl.pageNum = 1
pageControl.detailPage = 0
print("button pressed pageNum: \(pageControl.pageNum), startPage \(pageControl.detailPage)")
} label: {
mainListRow(actuator: actuators[0])
}
.buttonStyle(BorderlessButtonStyle())
Divider()
Button{
pageControl.pageNum = 1
pageControl.detailPage = 1
print("Rotate button pressed pageNum: \(pageControl.pageNum), startPage \(pageControl.detailPage)")
} label: {
mainListRow(actuator: actuators[1])
.background(.white)
}
.background(.black)
Divider()
Button{
pageControl.pageNum = 1
pageControl.detailPage = 2
print("Rotate button pressed pageNum: \(pageControl.pageNum), startPage \(pageControl.detailPage)")
} label: {
mainListRow(actuator: actuators[2])
.buttonStyle(BorderlessButtonStyle())
}
.background(.white)
and the the button page added to main here
struct testPage: View {
var body: some View {
MainList()
.frame(width: 330, height: 150)
.padding(.top, 20)
}
}
The gray/overlay on the button is from the button "tint".
To remove it, add the .plain modifier:
Button {
}
.buttonStyle(.plain)
As of today there is a bug in Xcode and that's why you can't see the tint properly in the simulator or the preview.
More examples of workarounds here:
https://hackingwithswift.com/quick-start/swiftui/how-to-disable-the-overlay-color-for-images-inside-button-and-navigationlink
in MainList get rid of:
.background(.white)

SwiftUI .contextMenu has visible padding around my View with a clipShape

I have an image in a chat bubble. This effect is achieved using a clipShape to a custom shape for the bubble.
When I attach a .contextMenu and present the contextMenu, it shows a padding around the chat bubble. Also the default cornerRadius of the contextMenu clips a small part of the chat bubble tip.
What I am trying to achieve is what I can see in the Apple Messages app. presenting contextMenu on a message with text preserves the clipShape of the text without adding padding. And on an image it removes the clipShape and resizes the image to a proper aspect Ratio. Neither of which I'm able to achieve.
struct ContentView: View {
var body: some View {
VStack {
Image("leaf")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 264, height: 361)
.clipShape(Bubble(location: .rightBottom))
.contentShape(Bubble(location: .rightBottom))
.contextMenu(ContextMenu(menuItems: {
Button {
} label: {
HStack {
Text("Save")
Image(systemName: "arrow.down.to.line")
}
}
}))
}
}
}
I have tried to use .contentShape to the same shape of the bubble but that had no effect at all. Is there anyway to show the contextMenu without the rounded corners and extra padding on the left side in screenshot below? Or to make the contextMenu containers background clear ideally?
Image prior to pressing context menu:
The solution was very simple. Just use content shape's first argument kind:
func contentShape<S>(_ kind: ContentShapeKinds, _ shape: S)
and set kind to .contextMenuPreview:
Image("leaf")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 264, height: 361)
.clipShape(Bubble(location: .rightBottom))
.contentShape(.contextMenuPreview, Bubble(location: .rightBottom))
.contextMenu(ContextMenu(menuItems: {
Button {
} label: {
HStack {
Text("Save")
Image(systemName: "arrow.down.to.line")
}
}
}))

Remove SwiftUI Form padding

When you use a SwiftUI Form it creates padding on the leading edge, which is fine for Form input but I would like to add additional content to the Form e.g. an image that takes the entire width of the screen, but because the image is in the Form, padding gets applied and the image gets pushed off screen slightly. How can I remove all Form padding?
struct MyForm: View {
var body: some View {
Form {
Image(uiImage: someImage)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: UIScreen.main.bounds.width)
TextField("Name", text: $name)
// Other fields
}
}
}
I know that the underlying view for Form is a UITableView where I can do things like UITableView.appearance().backgroundColor = .clear to change the Form appearance, but I can't figure out how to remove the leading padding.
I also know I can move the Image view outside the Form and put everything in a stack, but that creates other issues with scrolling that I'd like to avoid.
Here is a solution. Tested with Xcode 11.4 / iOS 13.4
Image(uiImage: someImage)
.resizable()
.aspectRatio(contentMode: .fill)
.listRowInsets(EdgeInsets()) // << this one !!
Note: hardcode to UIScreen.main.bounds.width is not needed

Avoid button styling its content in SwiftUI

I'm creating an iOS app using Apple's SwiftUI framework. As I need to detect if the user taps on a specific area of the screen, I obviously use a button.
The problem is that the area contains an Image and a Text, and as the button automatically gives its content the blue color, the image is also colored, so instead of being an Image it's just a blue rounded rectangle.
It is said that an image is worth a thousand words, and as I'm not good at explaining, here you have a graphic demonstration of what happens:
Outside the button (without button styling)
Inside the button (with button styling)
This happens because the button is adding .foregroundColor(.blue) to the image.
How can I avoid/disable the button adding style to its components?
EDIT: This is my button code:
ContentView.swift:
Button(action: {/* other code */}) {
PackageManagerRow(packageManager: packageManagersData[0])
}
PackageManagerRow.swift:
struct PackageManagerRow : View {
var packageManager : PackageManager
var body: some View {
VStack {
HStack {
Image(packageManager.imageName)
.resizable()
.frame(width: 42.0, height: 42.0)
Text(verbatim: packageManager.name)
Spacer()
Image(systemName: "checkmark")
.foregroundColor(.blue)
.opacity(0)
}.padding(.bottom, 0)
Divider()
.padding(.top, -3)
}
}
}
I think this is from the rendering mode for the image you are using.
Where you have Image("Cydia logo") (or whatever).
You should be setting the rendering mode like...
Image("Cydia Logo").renderingMode(.original)
You can also add a PlainButtonStyle() to your button to avoid iOS style behaviors.
Something like that with your example :
Button(action: {/* other code */}) {
PackageManagerRow(packageManager: packageManagersData[0])
}.buttonStyle(PlainButtonStyle())
I hope it will help you!
Another option is to not use a Button wrapper, but instead use tapAction directly on the Image to trigger your action when the image is pressed
HStack {
Button(action: {
print("Tapped")
}, label: {
Image("Logo").renderingMode(.original) // Add Rendering Mode
})
Text("Cydia")
}
A button with an icon! How original 😀.
If you are dealing with SF symbols then the following will do fine:
Button(action: addItem) {
Text(Image(systemName: "plus").renderingMode(.original))
+
Text("This is Plus")
}
.font(.system(size: 42))
The limitation of the option above is you don't have control over Image's size. So for custom images the following is more appropriate:
Button(action: addItem) {
Label(
title: { Text("Label").font(.system(size: 40)) }, // Any font you like
icon: { Image(systemName: "rectangle.and.pencil.and.ellipsis") // Both custom and system images, i.e. `Image("Cydia logo")`
.renderingMode(.original)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 42, height: 42) // Any size you like
.padding() // Any padding you need
} // and etc.
)
}
Apply the style .plain to your button to avoid overlay color.
// Before
Button(...)
// After
Button(...)
.buttonStyle(.plain) // Remove the overlay color (blue) for images inside Button
.plain button style, that doesn’t style or decorate its content while idle, but may apply a visual effect to indicate the pressed, focused, or enabled state of the button.
Another solution is to custom the style with ButtonStyle
like: struct MyButtonStyle:ButtonStyle { }
You have to render the original image by adding .renderingMode(.original) right after your image declaration.
Image("your_image_name")
.renderingMode(.original)

Resources