How to make the webview in full screen - ios

I have this code and I am trying to make the webview height to be a full screen
struct ContentView: View {
#State private var showWebView = false
private let urlString: String = "https://example"
var body: some View {
VStack(spacing: 40) {
// Normal WebView
WebView(url: URL(string: urlString)!).frame(height: 900)
.cornerRadius(10)
.shadow(color: .black.opacity(0.3), radius: 20.0, x: 5, y: 5)
}
}
}
I have tried to add to the height :
height: self.view.frame.height
but it didn't work

You should just add .ignoresSafeArea() modifier to your WebView:
struct ContentView: View {
#State private var showWebView = false
private let urlString: String = "https://example"
var body: some View {
VStack(spacing: 40) {
WebView(url: URL(string: urlString)!)
.cornerRadius(10)
.shadow(color: .black.opacity(0.3), radius: 20.0, x: 5, y: 5)
.ignoresSafeArea()
}
}
}
Also you can remove corenerRadius() modifier, because in this case it's not necessary.

To make a web view full screen with SwiftUI, you can use the .edgesIgnoringSafeArea(.all) modifier on the WebView object, like this:
WebView(url: url).edgesIgnoringSafeArea(.all)
This will make the web view occupy the entire screen, ignoring the safe area. The safe area is the part of the screen that is not obscured by the device's bezel or other device-specific elements. Ignoring the safe area will ensure that the web view takes up the maximum amount of space on the screen.
You can also use the .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity) modifier to achieve the same effect, like this:
WebView(url: URL(string: urlString)!)
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity
)
This will make the web view occupy the entire screen, regardless of the device's safe area.

Related

How to change the background color for the webview when loading in swiftUI

I have been trying to make the background color for the WebView to be black as the white doesn't look good.
struct ContentView: View {
#State private var showWebView = false
private let urlString: String = "https://example.com"
var body: some View {
VStack(spacing: 40) {
// Normal WebView
WebView(url: URL(string: urlString)!).frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity
)
}
}
I have tried to do this
VStack(spacing: 40) {
// Normal WebView
WebView(url: URL(string: urlString)!).frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity
)
}.background(Color.black)

SwiftUI - Fill image of background cell keeping aspect ratio

In the wonderful world of SwiftUI, I have a View that I use as a cell. I intend to reproduce the Layout of a previous application of mine, with Autolayout, not SwiftUI, in which the background image filled the entire cell, adjusting to the width and losing pieces of image above and below.
In my new app, the code in SwiftUI is the following:
struct CharacterRow2: View {
var character: Character
var body: some View {
Text(character.name)
.font(Font.custom(avengeanceHeroicAvengerNormal, size: 30))
.foregroundColor(.white)
.baselineOffset(-10)
.shadow(color: .black, radius: 1, x: -1, y: 1)
.frame(width: UIScreen.main.bounds.width, height: 140)
.background {
WebImage(url: extractImage(data: character.thumbnail))
.resizable()
.frame(width: UIScreen.main.bounds.width, height: 150)
}
}
}
With this code, my app looks like this:
I tried to add scaledToFill():
WebImage(url: extractImage(data: character.thumbnail))
.resizable()
.scaledToFill()
.frame(width: UIScreen.main.bounds.width, height: 150)
But this is the result:
I'm stuck...
Thank you in advance!
In this case, you are simply using too many frames. And using them incorrectly. You should avoid using UIScreen.main.bounds in SwiftUI, especially in something like a view cell. By doing this, the cell will not behave properly with other views, and can cause UI issues that would be difficult to trace.
The simplest way to get the behavior you want is this:
Text(character.name)
.font(.largeTitle)
.foregroundColor(.white)
.baselineOffset(-10)
.shadow(color: .black, radius: 1, x: -1, y: 1)
.frame(height: 140)
.frame(maxWidth: .infinity) // use .infinity for max width to make it
// as large as the space offered by the
// parent view
.background {
WebImage(url: extractImage(data: character.thumbnail))
.resizable()
// .frame(width: UIScreen.main.bounds.width, height: 150) <-- Remove this frame altogether
.scaledToFill()
}
.clipped // This keeps the image from being larger than the frame
This will size to be as wide as the parent view allows it to be. Leaving the UIScreen.main.bounds.width could cause it to be larger than the parent view and cause partial eclipsing.
In your last example the images are overlaying each other. This is due to calling scaleToFill(). The images are now ignoring their frame boundaries regarding the height. Adding .clipped solves the problem.
struct CharacterRow2: View {
var character: String
var body: some View {
Text(character)
.font(.largeTitle)
.foregroundColor(.white)
.baselineOffset(-10)
.shadow(color: .black, radius: 1, x: -1, y: 1)
.frame(width: UIScreen.main.bounds.width, height: 140)
.background {
Image(character)
.resizable()
.scaledToFill()
.frame(width: UIScreen.main.bounds.width, height: 150)
.clipped() // <-- Add this
}
}
}
It seems this will work only with a ForEach inside a ScrollView. Using a List seems to breack the vertical frame boundary.
struct ContentView: View{
let content = ["1.jpg", "2.jpg", "3.jpg" ]
var body: some View{
//These look really weird
// List(content, id: \.self){ name in
// CharacterRow2(character: name)
// }
// List{
// VStack{
// ForEach(content, id: \.self){ name in
// CharacterRow2(character: name)
// }
// }
// }
//working
ScrollView{
VStack{
ForEach(content, id: \.self){ name in
CharacterRow2(character: name)
.padding()
}
}
}
}
}
Result:

SwiftUI Sticky/Pinned Bottom Sheet View

I'm working on implementing a very simple bottom sheet in SwiftUI, but I am hitting an issue where I can't figure out how to get the bottom of the sheet to "stick" or "pin" to the bottom of the screen/view.
Here is the code I'm using for the bottom sheet
import SwiftUI
struct BottomSheetView<Content: View>: View {
#Binding var isOpen: Bool
#ViewBuilder let content: Content
#State private var contentSize: CGSize = .zero
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0.0) {
ChildSizeReader(size: $contentSize) {
content
}
}
.frame(
width: geometry.size.width,
height: contentSize.height,
alignment: .top
)
.clipShape(
Path(
UIBezierPath(
roundedRect: CGRect(x: 0.0, y: 0.0, width: geometry.size.width, height: contentSize.height),
byRoundingCorners: [.topLeft, .topRight],
cornerRadii: CGSize(width: 16.0, height: 16.0)
)
.cgPath
)
)
.frame(
height: geometry.size.height,
alignment: .bottom
)
.offset(y: isOpen ? 0.0 : contentSize.height)
.animation(
.spring()
.speed(1.25)
)
}
.edgesIgnoringSafeArea(.bottom)
}
}
struct BottomSheetView_Previews: PreviewProvider {
static var previews: some View {
BottomSheetView(isOpen: .constant(true)) {
Text("Bottom Sheet")
.frame(maxWidth: .infinity, minHeight: 200.0)
.background(
Rectangle()
.fill(Color.Primary.primaryRed)
)
}
}
}
And here is an image of the resulting view
The view as it sits statically is fine, but when the animation happens to open the bottom sheet, it bounces up and you can see below the view for a brief second. I imagine this will also become more apparent when I add a drag gesture to allow the user to interact with the bottom sheet (It's controlled by a button at the moment).
I'm looking for almost a stretchy view where when it is expanded open, at its max content height, the view can bounce up, and the bottom will stick to the bottom of the screen. Is this possible, and if so, how can I go about getting it to do that?

swiftUI transitions: Scale from some frame - like iOS Homescreen is doing when opening an App

How do I get the effect in a translation, that iOS home screen does when opening an App: It scales the App to fullscreen, starting from the App-icon on the home screen?
In my code, I have the icon-frame (CGRect) with position, width and height and I have the final frame. Is there a way to (probably combine some) transitions to get a scaling from the icon-frame to the final frame?
I get somewhat similar with:
view.transition(AnyTransition.scale(scale: 0, anchor: UnitPoint.trailing))
Which scales from zero to the original size, starting from trailing center position.
It's only close as:
it scales from zero instead of the size of the original icon
it starts from a fixed point (trailing, center) but I'd like to let it start from where the icon is.
Just to be sure: It must be a transition as the view is newly created and dropped. I tried with keeping the view and just change its opacity to show/hide it. With many other problems like not getting the reverse animation, when the view disappears.
Here is a demo of idea how such effect could be done with combined transitions... (positions and sizes are hardcoded for demo simplicity - they can be read with geometry reader, alignment guides or anchor preferences, and actually do not affect the transition usage idea, also animation can be configured)
Demo:
struct TestRisingView: View {
let screen = UIScreen.main.bounds
#State var showingView = false
#State var btFrame: CGRect = .zero
var body: some View {
GeometryReader { g in
ZStack {
Rectangle().fill(Color.clear)
self.activatingButton(frame: CGRect(x: 80, y: 30, width: 60, height: 40))
self.activatingButton(frame: CGRect(x: self.screen.maxX - 80, y: 30, width: 60, height: 40))
self.activatingButton(frame: CGRect(x: self.screen.maxX - 80, y: self.screen.maxY - 60, width: 60, height: 40))
self.activatingButton(frame: CGRect(x: 80, y: self.screen.maxY - 60, width: 60, height: 40))
if self.showingView {
self.topView
.zIndex(1)
.transition(
AnyTransition.scale(scale: 0.12).combined(with:
AnyTransition.offset(x: self.btFrame.origin.x - g.size.width/2.0,
y: self.btFrame.origin.y - g.size.height/2.0))
)
}
}
}
}
func activatingButton(frame: CGRect) -> some View {
Button(action: {
withAnimation {
self.btFrame = frame
self.showingView.toggle()
}
}) {
Text("Tap")
.padding()
.background(Color.yellow)
}
.position(frame.origin)
}
var topView: some View {
Rectangle()
.fill(Color.green)
.frame(width: 300, height: 400)
}
}
The above sample code should give you an idea on scaling an image
struct ImageCustomScaling: View {
// image to be scaled
var scaleImage: Image
// scale ratio
var scaleTo: Double
#State private var start = false
var body: some View {
VStack {
scaleImage
.font(.title)
.scaleEffect(self.start ? CGFloat(scaleTo) : 1)
.opacity(self.start ? 1 : 0)
.animation(Animation.interpolatingSpring(stiffness: 25, damping: 5, initialVelocity: 10).delay(0.9))
}
.foregroundColor(.blue)
.onAppear {
self.start = true
}
}
}
Above can be called by something like below
ImageCustomScaling(scaleImage: Image(systemName: "cloud.fill"), scaleTo: 5 )
Added animation modifier to give visual cue. You can change it to meet your needs.
Now in 2021 we finally got an answer from Apple. matchedGeometryEffect solves this problem elegantly and easily.
Here is a little screenshot of what it does:
And here is the code for this demo:
struct Stackoverflow: View {
#Namespace var namespace
#State private var shown: Bool = false
var body: some View {
VStack {
Button {
shown.toggle()
} label: {
Text("Tap Me!\nI'm a red square.")
.multilineTextAlignment(.center)
}
.background(
Color.red
.matchedGeometryEffect(id: "1", in: namespace, properties: .frame))
Spacer()
if shown {
Color.clear
.aspectRatio(contentMode: .fit)
.matchedGeometryEffect(id: "1", in: namespace, properties: .size)
.border(Color.gray)
}
Spacer()
}
.animation(.easeInOut(duration: 1))
}
}
struct Stackoverflow_Previews: PreviewProvider {
static var previews: some View {
Stackoverflow()
}
}
Check here on swift-lab.com, for more information and explanation on how to use matchedGeometryEffect.

How to get the iPhone's screen width in SwiftUI?

I want to resize an Image frame to be a square that takes the same width of the iPhone's screen and consequently the same value (screen width) for height.
The following code don't work cause it gives the image the same height of the view.
var body: some View {
Image("someImage")
.resizable()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
.clipped()
}
You can create a UIScreen extension for the same. Like:
extension UIScreen{
static let screenWidth = UIScreen.main.bounds.size.width
static let screenHeight = UIScreen.main.bounds.size.height
static let screenSize = UIScreen.main.bounds.size
}
Usage:
UIScreen.screenWidth
Try using Geometry Reader
let placeholder = UIImage(systemName: "photo")! // SF Symbols
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
Image(uiImage: placeholder)
.resizable()
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
// .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
.clipped()
}
}
}
You can use UIScreen.main.bounds .width or .height
.frame(
width:UIScreen.main.bounds.width,
height:UIScreen.main.bounds.height
)
I've come up with a solution using Environment Keys, by creating the following:
private struct MainWindowSizeKey: EnvironmentKey {
static let defaultValue: CGSize = .zero
}
extension EnvironmentValues {
var mainWindowSize: CGSize {
get { self[MainWindowSizeKey.self] }
set { self[MainWindowSizeKey.self] = newValue }
}
}
Then by reading the size from where the window is created:
var body: some Scene {
WindowGroup {
GeometryReader { proxy in
ContentView()
.environment(\.mainWindowSize, proxy.size)
}
}
}
Finally I can directly get the window size in any SwiftUI view, and it changes dynamically (on device rotation or window resizing on macOS):
#Environment(\.mainWindowSize) var mainWindowSize
The simplest way would be to make the image resizable and set the aspect ratio to 1.0:
var body: some View {
Image("someImage")
.resizable()
.aspectRatio(1.0, contentMode: .fit)
}

Resources