I was experimenting with swiftUI and i found out that cornerRadius attributes makes my image disappear.
Image(uiImage: content.image)
.resizable()
.scaledToFill()
.frame(width: 60, height: 40)
.cornerRadius(3)
Is there something wrong with the code here ?
I want my image to be resizable, fill the whole view, have a dimension of 60x40 and a cornerRadius of 3.
Thanks for your help, if you know what's wrong :)
XCode: 12.1 (12A7403)
I came across the same problem and I think it is either a bug, or a something new that came with Xcode 12.1, because it was working perfectly a few weeks back. I did try a lot of ways to put the .cornerRadius() but it always resulted in the Image vanishing. This is my final solution, but I do believe .cornerRadius() would be better here. I hope it will help you! The color of the stroke will need to be the color of your background.
Image("MyImage")
.resizable()
.frame(width: 19, height: 19, alignment: .center)
.overlay(RoundedRectangle(cornerRadius: 4)
.stroke(Color.white, lineWidth: 3.3)
)
I have met similar problems. When I set the cornerRadius for a view on simulator, the view disappear. However, when running on the real device, the view shows up. I think it is a bug of Xcode, you may test on real device to see your view.
Here's how I worked around this.
I made an equivalent of ImageView with UIKit.
struct FixImageView: UIViewRepresentable {
let image: UIImage
let contentMode: UIView.ContentMode
final class Coordinator: NSObject {}
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeUIView(context: Context) -> UIImageView {
let view = UIImageView()
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
view.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
view.image = image
view.contentMode = contentMode
return view
}
func updateUIView(_ uiView: UIImageView, context: Context) {
uiView.image = image
uiView.contentMode = contentMode
}
}
You can use it just like a normal SwiftUI's Image.
#ViewBuilder
var body: some View {
FixImageView(image: image, contentMode: .scaleAspectFit)
.cornerRadius(20)
}
If you're wondering why the the compression priority is low, Tomas Linhart explains:
SwiftUI uses the compression resistance priority and the content hugging priority to decide what resizing is possible.
If you want to resize a view below its intrinsic content size, you need to reduce the compression resistance priority.
Related
My project is target for iOS 13 onwards hence could not use PageTabViewStyle(). I tried with Mr. John suggestion from this link
SwiftUI create image slider with dots as indicators
Now I need to open the detail view on clicking the image in the slideshow. But now when I try to move the images the detail view is opening and could not move the images as in Pageview. I implemented the below code. Please let me know the solution to fix this. Thanks in advance.
PagingView(index: $index.animation(), maxIndex: images.count - 1){
ForEach(articles, id: \.self) { article in
NavigationLink(destination: ArticleDetailUIView(article: articles[self.index], isBookmark: false) , isActive: $areYouGoingToArticleView)
{
Image(article.image)
.resizable()
.scaledToFill()
.accessibility(identifier: "articleImage")
}
}
}
.aspectRatio(4/3, contentMode: .fit)
.clipShape(RoundedRectangle(cornerRadius: 15))
After few browsing, the workaround was using the stepper to move around the slide show instead of scrolling the images. I added the below lines.
Stepper("Index: (index)", value: $index.animation(.easeInOut), in: 0...images.count-1)
.font(Font.body.monospacedDigit())
.labelsHidden()
I cannot understand why in the simulator the layout is different from the layout displayed in xcode/preview.
struct ContentView : View {
var body: some View {
ZStack(alignment: .bottom) {
ARViewContainer()
Text("hello")
}
}
}
here the screenshots:
TLDR; I don't know why your simulator and preview don't match, but I do know why it's appearing the way that it is on the device. Are you getting any errors in the debug?
Any container views in SwiftUI will only take up the required space that they need. they will also distribute according to your settings. For example, you have a ZStack that contains a bottom alignment. You also have a ARViewContainter() that takes up a portion of that stack. They are aligned behind each other on the Z axis where the text is in front and the other container is behind. A quick way to prove this and test it is to include a background shape behind everything for example.
var body: some View {
ZStack {
Rectangle().edgesIgnoringSafeArea(.all)
.foregroundColor(Color.red)
//Your Views
}
}
This will force the ZStack to take up all available space and your other views should then align as expected. Basically, your Text is aligning to the bottom of the maximum provided space, which is being provided by your ARViewContainer()
Further Reading and Understanding
Views only take up the space required for what's in them, unless otherwise specified.
ZStacks operate on the Z axis, forward/backwards.
In your case you have a view with a set size called ARViewContainer() which takes up the width of the screen and a portion of the height. Since it's the largest view you have, the ZStack inherits that size.
Your text is smaller than the ZStack so the ZStack does NOT inherit the size. You do however have a .bottom assignment. So your text is over your ARViewContainer() and aligned to the .bottom edge of that container.
Finally the ZStack is centered in the remaining space available, giving it the impression that your .bottom isn't doing anything, when in reality it is.
Reproducing the Issue
Here is a code snippet that reproduces your issue and makes it a bit clearer and easier to understand.
var body: some View {
ZStack(alignment: .bottom) {
Rectangle().foregroundColor(Color.yellow).frame(width: 100, height: 100, alignment: .center)
Text("Test")
}
}
Using SwiftUI, I have a List of items retrieved from CoreData. Everything I read makes it look like the list should be something I can scroll normally, but in both the simulator and running on an iPad, the List doesn't scroll.
Clearly there is some piece missing, but I can't seem to find what it is. The list populates correctly, but it simply won't scroll.
struct PeopleList : View {
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(
entity: Person.entity(),
sortDescriptors: [
NSSortDescriptor(keyPath: \Person.lastName, ascending: true),
NSSortDescriptor(keyPath: \Person.firstName, ascending: true)
]
) var people: FetchedResults<Person>
var body: some View {
ZStack(alignment: .topLeading) {
List(self.people, id: \.self) { person in
Text(person.descriptionForList())
}
}
.frame(width: 400, height: 200)
.modifier(RoundedEdge(width: 4, color: Color(red: 0.6, green: 0.6, blue: 0.6), cornerRadius: 10))
.padding(10)
}
}
Things that didn't change anything:
Removing the ZStack { }
Replacing the List with a simple List(0...100, id: \.self) { item in Text("hey \(item)") } -- still won't scroll.
Adding a .frame() to the List() itself.
(added) removing the .frame .modifier and .padding all accomplished nothing.
This is not about (the following topics have Q&A's addressing them, but not this issue):
programmatic scrolling
disabling scrolling
scroll indicators
ScrollView
UIKit
How can I track down what might be preventing the List() from scrolling?
Turns out, the issue wasn't with anything in the struct that was posed above; it all had to do with the View where that struct was being used. Specifically, when a Color() -- even of .opacity(0) -- was in a ZStack sitting "above" the List(), the latter stops scrolling. Displays fine, but just won't scroll.
This question describes the same thing happening, albeit in somewhat different circumstances.
I'll leave this question up, since somebody else may be at the same place I was, "why isn't my List() scrolling?", rather than "why isn't my List() in a ZStack scrolling?" Hopefully a version of Swift later than 13 will fix this behavior!
I have a button and I'd like to put a semi-transparent gradient overlay on top of it.
Button(action: {
print("Pressed")
}) {
Text("Press me")
}
.overlay(
LinearGradient(
gradient: Gradient(colors: [.clear, Color.black.opacity(0.3)]),
startPoint: .top,
endPoint: .bottom
).disabled(true)
)
Even though the gradient has disabled(true) it still eats the touches and doesn't forward them to the actual button. .allowsHitTesting(false) provides the same result.
Any idea what can be wrong?
Note: I know I can put the overlay just to Text("Press me") but I don't want it. (This is just example code showcasing the problem)
Edit: This issue is solved in Xcode 11.2 ✅
The following code works on Xcode 11.2 / iOS 13.2
struct TestButtonWithOverlay: View {
var body: some View {
Button(action: {
print("Pressed")
}) {
Text("Press me")
.padding()
}
.overlay(
LinearGradient(
gradient: Gradient(colors: [.clear, Color.black.opacity(0.3)]),
startPoint: .top,
endPoint: .bottom
)
.allowsHitTesting(false) // !!! must be exactly here
)
}
}
When working with a ScrollView the correct fix is to use the .background content modifier on the ScrollView:
https://developer.apple.com/documentation/swiftui/form/background(alignment:content:)
It will as expected with your "background" appearing above the content of the ScrollView.
Example of using to create a shadow effect:
Interesting note: On the simulator .overlay works with a ScrollView, but on a physical device only .background allows scrolling.
The physical device is on iOS 14, and the simulator is iOS 15, so it's possible it might have been a regression. Although apparently this issue dates back further than either.
I'm embedding a view controller with variable-height UITextView inside a parent SwiftUI VStack and the view controller sizes it's frame to the whole screen between viewDidLoad and viewDidLayoutSubviews. The UITextView expands only to the size of the text inside itself and centers itself inside the parent view.
I'm trying to add this view controller in a VStack and have it behave externally like other SwiftUI components do - sized exactly to the content it contains - but it wants to be sized to the whole screen minus the other VStack elements.
I can get the correct size of the UITextView in didLayoutSubviews and pass it upwards to SwiftUI where it can be set properly - but where do I do that?
In the example screenshot below, the orange is the embedded UIView background, the green is the UITextView and the VStack looks like this:
VStack {
HighligherVC()
Text("Tap and drag to highlight")
.foregroundColor(.gray)
.font(.caption)
}
Without being able to see more of your code, it's slightly difficult to say what the best solution would be, but based purely on this part of your question...
I can get the correct size of the UITextView in didLayoutSubviews and pass it upwards to SwiftUI where it can be set properly - but where do I do that?
I would suggest that you pass a binding property to your view controller that can be set to the calculated text view height, meaning that the view that contains your VStack would have a #State property like this:
#State private var textViewHeight: CGFloat = 0
You would then declare a #Binding property on your HighlighterVC and add an initializer like this:
#Binding var textViewHeight: CGFloat
init(textViewHeight: Binding<CGFloat>) {
self._textViewHeight = textViewHeight
}
And then you would set textViewHeight to the calculated height in your didLayoutSubviews and add a .frame modifier to your HighlighterVC like this:
VStack {
HighlighterVC(textViewHeight: self.$textViewHeight)
.frame(height: self.textViewHeight)
Text("Tap and drag to highlight")
.foregroundColor(.gray)
.font(.caption)
}
Like I said at the beginning of my answer, this solution (that I believe would work, but since I can't test it, I'm not 100% certain) is based on your thoughts about what it is that you need. Without seeing more code, it's impossible for me to say if this is the best solution.
Add fixedSize may solve this.
.fixedSize(horizontal: false, vertical: true)