SwiftUI border around dynamically shaped View - swiftui

Sometimes a view's shape can be dynamic like when using the native .buttonStyle(.bordered) modifier. This modifier will apply different corner radius based on different button content sizes:
Question
How can we stroke around a custom view? (not a Shape)
Considerations after trials:
We can't use another button with bigger/smaller frame to create the border illusion, because the corner radius will be different and not matched with the original shape
The view has transparency, so using shadow effect may not be a good option to create the border illusion
Using scale effect not works as desired, because of the different sizes issue, and also the lack of precise border width
ContainerRelativeShape is not working (yet) except for widgets
Not found solutions to try:
Get the shape of a view to apply stroke on it
Get the path around the content of the view
apply some sort of stroke directly on a view
Sample Code:
VStack {
Button {
} label: {
Text("Hello World")
.frame(height: 164) // <- Difference
}
Button {
} label: {
Text("Hello World")
.frame(height: 64) // <- Difference
}
}
.buttonStyle(.bordered)
/* .stroke() // something like this, but for a view */

Unfortunately, borders are drawn around the frame of a view. If you want to add a border to a rounded view, you need to know the corner radius.
You can hardcode the corner radius of your buttons using the .buttonBorderShape() modifier, then overlay your buttons with a shape that matches the shape you choose in .buttonBorderShape()

Related

how to match the font of an existing view

I'm curious, is there a way to make the fonts in a view match those of an existing view in SwiftUI? I don't like the default selections of swiftUI in a certain context, and I'd like some control over the situation.
Here's some code to illustrate:
struct FontMatchView: View {
var body: some View {
Form {
Section {
Text("Some Controls Here")
} header: {
HStack {
Text("Header")
Spacer()
Button("Option") {
}
}
}
}
}
}
This gives this result:
In the Section Header, I'd like the font in the button on the right (with label "OPTION") to match the label to its left ("HEADER"). I'm guessing this will be hard because the font is not known at the time of view definition. But the choices SwiftUI has made here are "clearly wrong" :-), and I need to fix this.
Is there a way we solve this (other than overriding both fonts)? Ideally, I could say "use a font that is 0.8 x the height of whatever font will be used in view X". But I'd settle for "use the same font as will be used in view X".
You can remove "buttonizing" (which includes adjusting the font) by applying .buttonStyle(.plain). This will make it match the other Text in the current context. If you then want to re-accent it, you may:
Button("Option") {}
.buttonStyle(.plain)
.foregroundColor(.accentColor)
That said (and somewhat unrelated), making the button as small as the HEADER text may make it uncomfortably small as a hit area. It may be better to make HEADER larger rather than OPTION smaller.

SwiftUI: How to make a transparent Rectangle (.fill(.clear)) receive gestures?

I would like to overlay my image with several Rectangle()s, that should respond to gestures (like tapping or dragging). However, I found that when I make the rectangle clear, it stops receiving gestures.
Rectangle()
.fill(.clear)
.gesture(
LongPressGesture()
.onEnded { value in
// this isn't called when the rectangle fill is .clear
}
)
Is there a way to let an invisible element receive taps? I know that I could give it a 1 % opacity, but that feels like an ugly (and visible) kludge.
Add content shape that defines hit-testable area, like
Rectangle()
.fill(.clear)
.contentShape(Rectangle()) // << here !!
.gesture(
One extra way apart from #Asperi's answer is
Color.clear.contentShape(Rectangle())

Swift UI padding on Navigation "back" button

How do I adjust the padding of a Swift UI "back" navigation button? i.e. the blue "Navigation" text in the image below (Image contributed by someone else on a different question). I want to add more space between the text and the leading edge of the screen.
You can use custom appearance for this purpose configured in the init of view holding NavigationView.
Here is a demo (with big offset for better visibility). Prepared with Xcode 13 / iOS 15.
init() {
let appearance = UINavigationBarAppearance()
appearance.backButtonAppearance.normal.titlePositionAdjustment =
UIOffset(horizontal: 40, vertical: 0)
UINavigationBar.appearance().standardAppearance = appearance
}

SwiftUI - ScrollView move content to be visible with KeyboardAwareSwiftUI

I found this answer great for views but for a scrollview it works with this half text view height effect:
Is this something I can do with this KeyboardAwareSwiftUI classes? I tried to play with magical numbers to increase this values here:
func body(content: Content) -> some View {
content
.padding(.bottom, self.keyboard.height + 100)
.edgesIgnoringSafeArea(self.keyboard.height > 0 ? .bottom : [])
.animation(.easeOut)
}
but this just increased some area above the keyboard but text view is still hidden a bit:
I would recommend using this library instead, and you never have to worry about view positioning when keyboard is shown: https://github.com/hackiftekhar/IQKeyboardManager
It's a non swiftui library, however, this issue here shows how to add it to your swiftui app seamlessly and only a few line of code:
https://github.com/hackiftekhar/IQKeyboardManager/issues/1606
hope this helps

Add UINavigation Back button in UICollectionView with Swift 3

I add Left Navigation Back button in collection view controller with code.
//Add Navigation Bar
navbar.autoresizingMask = [.flexibleWidth, .flexibleBottomMargin, .flexibleRightMargin]
navbar.delegate = self
UINavigationBar.appearance().barTintColor = UIColor(red: 0.0/255.0, green:49.0/255.0, blue:79.0/255.0, alpha:0.1)
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().isTranslucent = true
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName : UIColor.white]
navItem.title = prefs.value(forKey: "PROVIDER_NAME") as! String?
let image = UIImage(named: "back_image")
navItem.leftBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(addTapped))
navItem.leftBarButtonItem?.imageInsets = UIEdgeInsetsMake(0, 0, 0, 0)
Back button is so close to the left. I would like to add padding about 10px from the left. So, I changed the code into
navItem.leftBarButtonItem?.imageInsets = UIEdgeInsetsMake(0, 15, 0, 0)
but it is not working and image Back button looks smaller. How can I do to add space to the left of Back button?
I would recommend replacing UINavigationBar with a simple UIView. This way you would gain a full control over the layout of the navigation bar. It wouldn't be anything more than a transparent UIView with a back button and a title label inside. As simple as that.
The real UINavigationBar is more than that. It's meant to manage a stack of UINavigationItem objects. It adjusts itself depends on the current item and knows how to make an animated (and even interactive) transition from one state to another. That's why you can't change much about the bar's appearance. You shouldn't treat it as a regular view.
UPDATE
Another way to achieve this is a little tricky. You can implement it completely from a storyboard and you don't need mess with appearance.
Add UINavigationBar to a view controller.
Add a plain UIView to the left side of UINavigationBar and make its background color completely transparent.
Add UIButton to the view added in the previous step and set a back icon as its image.
Add constraints to the button to align it to the right side of its superview.
Adjust the width of the view so the back button position is exactly where you want it to be.
This is a view hierarchy in the storyboard:
This is how your UINavigationBar will look like (for you the background will be transparent):