I am just starting out with both Watch development and SwiftUI, and thought I would start with a simple Login Screen. I have made two buttons of two different styles. The strange thing is that my buttons have a strange red inner view to them and I am not sure why.
struct ContentView : View
{
var body: some View
{
VStack
{
Button( "Login")
{
}
.accentColor( .white)
.frame( idealHeight:50.0)
.padding( [.leading, .trailing], 10.0)
.background( Color.red)
.cornerRadius( 5.0)
Button( "Sign Up")
{
}
.accentColor( .red)
.frame( idealHeight:50.0)
.padding( [.leading, .trailing], 10.0)
.background( Color.white)
.cornerRadius( 5.0)
}
}
}
Can someone tell me what is going on here ?
Also if someone has reputation of 1500 can they please create WatchOS6 tag ?
Update: This works way better on the iPhone than the Watch, Buttons seem to work differently on the two devices. As pointed out by #MarkT you need to start out with a button style of plain. The issue with this is that it stops you using your own Button Styles.
You are only defining a background behind a "default" Button that's why it looks so wired.
Changing the button style to "plain" and apply the corner radius to the background will help. Of course you must adjust the background size by using padding to frame ...
Button( "Login")
{
}
.buttonStyle(PlainButtonStyle())
.padding(.horizontal, 60)
.padding(.vertical, 10)
.background(
Color.red
.cornerRadius( 5.0))
Update: As mentioned from d4Rk we have to use PlainButtonStyle()
This worked for me
HStack {
Button("Login")
{
}
.buttonStyle(BorderedButtonStyle(tint: .clear))
.foregroundColor(.white)
}
.background(Color.red)
.cornerRadius(5)
if its in a list try this as well
.listRowPlatterColor(Color.clear)
I believe that this is a result of using accentColor to define the colour of you text for the button.
You can substitute this for foregroundColor and achieve the same text colour but hopefully without the odd patterning you are currently experiencing.
With the changes your code would look like:
struct ContentView : View
{
var body: some View
{
VStack
{
Button( "Login")
{
}
.foregroundColor( .white)
.frame( idealHeight:50.0)
.padding( [.leading, .trailing], 10.0)
.background( Color.red)
.cornerRadius( 5.0)
Button( "Sign Up")
{
}
.foregroundColor( .red)
.frame( idealHeight:50.0)
.padding( [.leading, .trailing], 10.0)
.background( Color.white)
.cornerRadius( 5.0)
}
}
}
Unfortunately this doesn't entirely solve the issue.
As you can see we go from this:
to this:
Which is not ideal.
According to this documentation you should set the radius to 22 points (or 9 for scroll view)
Related
I want to set the users Profile Picture as the item Image of the Tabbar.
What I tried so far is following:
ProfileView()
.tag(3)
.tabItem {
if selectedTab == 3 {
Image("1024")
.resizable()
.scaledToFit()
.frame(width: 20.0, height: 20.0)
} else {
Image(systemName: "bell")
}
}
It also shows the Image, but way to big - it doesn't resize to the 20x20...
Am I missing something? .renderingMode(.template) doesn't help as well
Why is there so much space between the three blue rectangles and the list? How can I remove the space so that all views within the VStack stack at the top? I tried using a Spacer() directly after the List, but nothing changed.
struct ContentView: View {
init() { UITableView.appearance().backgroundColor = UIColor.clear }
var body: some View {
NavigationView {
ZStack {
Color.red
.ignoresSafeArea()
VStack {
HStack {
Text("Faux Title")
.font(.system(.largeTitle, design: .rounded))
.fontWeight(.heavy)
Spacer()
Button(action: {
// settings
}, label: {
Image(systemName: "gearshape.fill")
.font(.system(.title2))
})
}
.padding()
GeometryReader { geometry in
HStack() {
Text("1")
.frame(width: geometry.size.width * 0.30, height: 150)
.background(Color.blue)
Spacer()
Text("2")
.frame(width: geometry.size.width * 0.30, height: 150)
.background(Color.blue)
Spacer()
Text("3")
.frame(width: geometry.size.width * 0.30, height: 150)
.background(Color.blue)
}
}
.padding()
List {
Text("One")
Text("Two")
Text("Three")
Text("Four")
Text("Five")
Text("Six")
}
.listStyle(InsetGroupedListStyle())
}
}
.navigationBarHidden(true)
}
}
}
Bonus question: In web development, you can open your browser's Web Inspector and use the element selector to click on elements which highlights their borders. Useful for something like this where you're trying to figure out which element the offending spacing belongs to. Is there something like that in Xcode?
VStack(spacing: 0) {...}
Spacer()
to your question you can in Xcode use the view inspector. https://developer.apple.com/library/archive/documentation/ToolsLanguages/Conceptual/Xcode_Overview/ExaminingtheViewHierarchy.html
Since you know that your HStack with the blue rectangles is going to be a height of 150, you should constrain it to that using .frame(height: 150):
GeometryReader { geometry in
...
}
.padding()
.frame(height: 150) //Here
Otherwise, the GeometryReader will occupy all available vertical space.
Re: your web dev comparison, check out the Xcode view hierarchy inspector. It's not exactly the same, but it's in the same vein: https://developer.apple.com/library/archive/documentation/ToolsLanguages/Conceptual/Xcode_Overview/ExaminingtheViewHierarchy.html
I have a rectangle with a shadow and a context menu. When I close this context menu the shadow of the rectangle appears with a delay (~0.5 seconds). Both the shadow of the complete rectangle as well the shadow of the inner elements. I'm not sure what I am doing wrong.
struct Playground: View {
var body: some View {
VStack(alignment: .leading, spacing: 4.0) {
Spacer()
HStack {
Spacer()
Image(systemName: "house")
.font(.system(size: 50))
Spacer()
}
Text("SwiftUI for iOS 14").fontWeight(.bold).foregroundColor(Color.white)
Text("20 Sections").font(.footnote).foregroundColor(Color.white)
}
.frame(width: 200, height: 300)
.padding(.all)
.background(Color.blue)
.cornerRadius(20.0)
.shadow(radius: 10)
.contentShape(RoundedRectangle(cornerRadius: 20))
.contextMenu(menuItems: {
Text("Menu Item 1")
Text("Menu Item 2")
Text("Menu Item 3")
})
}
}
I found a hacky way to mitigate the issue. This is using pure UIKit and Objective-C but it might be able to be done in SwiftUI too in one way or another.
This is a temporary direct-via-subview access to the view that animates, and I simply attach a shadow to it. This way the animating view will have a shadow and when the REAL shadow pops in, it does so below this shadow. The result looks fine.
-(void)contextMenuInteraction:(UIContextMenuInteraction *)interaction
willEndForConfiguration:(UIContextMenuConfiguration *)configuration
animator:(id<UIContextMenuInteractionAnimating>)animator {
// TODO: Find view dynamically /safer
UIView *platter = (UIView*)self.view.window.subviews[1].subviews[1].subviews[0];
platter.clipsToBounds = NO;
platter.layer.shadowOffset = CGSizeMake(0, 0);
platter.layer.shadowRadius = 5;
platter.layer.shadowOpacity = 0.5;
platter.layer.shadowColor = [UIColor blackColor].CGColor;
}
This works in iOS 13. Have not tried on iOS 14.
Added on the 24th of July:
This line of code fixes the space in the detail view. However... in the list view the title has become a lot smaller too.
.navigationBarTitle(Text("Egg management"), displayMode: .inline)
Added on the 23th of July:
Thanks to the tips I made a lot of progress. Especially the tip to add borders does wonders. You see exactly what happens!
However, there seems to be a difference between the Xcode Preview canvas, the simulator and the physical device. Is this a bug because -after all- it is still beta? Or is there anything I can do?
As you can see in the images... only in the Xcode Preview canvas the view connects to the top of the screen.
I believe it has something to do with the tabbar. Since when I look at the Xcode Preview canvas with the tabbar... that space above is also there. Any idea how to get rid of that?
Original postings:
This is my code for a detailed list view:
import SwiftUI
struct ContentDetail : View {
#State var photo = true
var text = "Een kip ..."
var imageList = "Dag-3"
var day = "3.circle"
var date = "9 augustus 2019"
var imageDetail = "Day-3"
var weight = "35.48"
var body: some View {
VStack (alignment: .center, spacing: 10) {
Text(date)
.font(.title)
.fontWeight(.medium)
ZStack (alignment: .topLeading){
Image(photo ? imageDetail : imageList)
.resizable()
.aspectRatio(contentMode: .fit)
.background(Color.black)
.padding(.trailing, 0)
.tapAction {
self.photo.toggle() }
HStack {
Image(systemName: day)
.resizable()
.padding(.leading, 10)
.padding(.top, 10)
.frame(width: 40, height: 32)
.foregroundColor(.white)
Spacer()
Image(systemName: photo ? "photo" : "pencil.circle")
.resizable()
.padding(.trailing, 10)
.padding(.top, 10)
.frame(width: 32, height: 32)
.foregroundColor(.white)
}
}
Text(text)
.lineLimit(6)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 6)
} .padding(20)
}
}
#if DEBUG
struct ContentDetail_Previews : PreviewProvider {
static var previews: some View {
ContentDetail()
}
}
#endif
Also included is the preview canvas. What I don't get is how I can make sure the text and photo are aligned to the top (instead of the middle). I tried with Spacers, padding etc.
I must be overseeing something small I guess... but. Can somebody point me in the right direction? Thanks.
Added:
After both answers I added a Spacer() after the last text. In Xcode in the preview canvas everything looks okay now. But on my connected iPhone 7 Plus there are some problems: the view is not aligned to the top, and the image is cropped (icon on the right is gone; white banding to the right).
Adding a Spacer() after the last text shifts everything to the top. Tested on iPhone Xr simulator (not preview).
...
Text(text)
.lineLimit(6)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 6)
Spacer()
}
To remove the space at the top:
VStack {
...
}
.padding(20)
.navigationBarTitle("TITLE", displayMode: .inline)
Think in terms of what a Spacer() does. It "moves" the views as far apart as it can - at least, without a specific space.
So you have this:
VStack {
Text
ZStack {
Image
HStack {
Image
Spacer()
Image
}
}
Text
}
All told, going from inner to outer, you have a horizontal stack of two images placed as far apart (the spacer is between them) inside of a "Z axis" stack that places an image on top of them, inside of a vertical stack that has some text above it.
So if you want to move everything in that vertical stack to the top, you simply need to add one last spacer:
VStack {
Text
ZStack {
Image
HStack {
Image
Spacer()
Image
}
}
Text
Spacer() // <-- ADD THIS
}
Last note: Don't be afraid to adding additional "stacks" to your view. In terms of memory footprint, it's really just a single view with no performance hit.
EDIT: I took your original view and changed everything to placeholders...
var body: some View {
VStack (alignment: .center, spacing: 10) {
Text("Text #1")
.font(.title)
.fontWeight(.medium)
ZStack (alignment: .topLeading) {
Text( "Image #1")
HStack {
Text("Image #2")
Spacer()
Text("Image #3")
}
}
Text("Text #2")
.lineLimit(6)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
.padding(.leading, 6)
} .padding(20)
}
As expected, everything is vertically centered. Adding a Spacer() below "Text #2" throws everything to the top. A couple of thoughts:
Starting there, and add in your Image views one by one. Add in the modifiers like that also.
I don't have the specific images you are rendering, so maybe put a noticeable background color on various things (orange is my personal favorite) and see if the top Image is actually on top but the image makes it appear as though it isn't. A border would work pretty well too.
I'm trying to center a bunch of views in a VStack within a ScrollView in SwiftUI. To simplify things, I'm just trying to get it to work with a single Text view. Here's what I've come up with so far:
var body: some View {
ScrollView(alwaysBounceVertical: true){
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
Spacer()
} //HStack
.background(Color.green)
} //ScrollView
.background(Color.gray)
}
This results in this:
I want the text to be in the middle like this:
So the HStack should be full-width and the Text should be centered within it. It seems like this should be easy, but I don't get what I'm doing wrong. :)
Using GeometryReader, you can get information about the size of the containing view and use that to size your view.
var body: some View {
GeometryReader { geometry in <--- Added
ScrollView(alwaysBounceVertical: true){
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
Spacer()
} //HStack
.frame(width: geometry.size.width) <--- Added
.background(Color.green)
} //ScrollView
.background(Color.gray)
}
}
edit: after looking into this more, it seems that part of the problem is that you are using a ScrollView. If you remove that parent, the spacers in the HStack will automatically cause stretching to fill the view. I'm guessing the automatic stretching doesn't happen in ScrollViews because there's no finite limit to how big it can be, how much would it stretch? (because a ScrollView can scroll in any direction)
This seems to be a bug in Xcode 11.0 beta, ScrollView content wouldn't fill the scroll view. If you replace the ScrollView with a List it will work as expected. But if you have to use a scroll view, one workaround is to fix the scroll view's content width.
So your code will look something like this:
ScrollView(alwaysBounceVertical: true) {
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
Spacer()
} // HStack
.frame(width: UIScreen.main.bounds.width) // set a fixed width
.background(Color.green)
} // ScrollView
.background(Color.gray)
Result:
You should use the modifier frame and set its maxWidth: .infinity.
So it tells its parent: "the wider, the better" :)
var body: some View {
ScrollView(.vertical, showsIndicators: true){
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
.frame(maxWidth: .infinity) // <- this
Spacer()
} //HStack
.background(Color.green)
} //ScrollView
.background(Color.gray)
}
And this works regardless its parent.
Scrollview or whatever View it's set in.
Paul is doing a great job clarifying it to all of us here:
https://www.hackingwithswift.com/quick-start/swiftui/how-to-give-a-view-a-custom-frame
Answer compatible with Xcode 12.1 (12A7403)
I hope this helps 👍
dsa