Center MPVolumeView vertically in SwiftUI view - swiftui

I want to place an MPVolumeView in my SwiftUI view but it doesn't behave like a normal SwiftUI view. I want the volume slider to be centered vertically between the two dividers. If you replaced VolumeSlider with Text it would be centered. How can I make the VolumeSlider behave in the same way?
// Must be run on real device, not simulator
import SwiftUI
import MediaPlayer
struct ContentView: View {
var body: some View {
VStack {
Divider()
VolumeSlider()
.frame(height: 128)
Divider()
}
}
}
struct VolumeSlider: UIViewRepresentable {
func makeUIView(context: Context) -> MPVolumeView {
MPVolumeView(frame: .zero)
}
func updateUIView(_ view: MPVolumeView, context: Context) {}
}

Firstly this behavior is in fact normal for a SwiftUI view. You are specifying a point height for the VolumeSlider. If you decrease the 128 it will not have the space between slider and bottom divider.
struct ContentView: View {
var body: some View {
VStack {
Divider()
VolumeSlider()
.frame(height: 50)
Divider()
}
}
}
I'll update this answer to try and make it not take up the whole space when .frame is removed.

Related

SwiftUI Is there any built in view that kind of slides in from the side and takes up 3/4 of the screen?

This is probably a custom view but in the Reddit app there's a toolbar and the top left button(3 lines) opens this kind of view from the side that moves the current view to the right so you can only see about 25% of it and a new view that takes up about 75% of the screen slides in. Is there anything like this built into SwiftUI and if there isn't how would I go about implementing something like this?
This is my custom side bar behave similarly to what you just mentioned, you can try it. (Images and Code are below)
Before click:
After clicked:
struct ContentView: View {
#State var isClicked = false
var body: some View {
HStack {
Rectangle()
.fill(.orange)
.frame(width: isClicked ? UIScreen.main.bounds.width * 0.75 : 0)
VStack {
HStack {
Button {
withAnimation {
isClicked.toggle()
}
} label: {
Image(systemName: "menucard.fill")
.padding(.leading)
}
Spacer()
}
Spacer()
}
}
}
}

View resizing when hiding tab bar with Introspect in SwiftUI

I'm using Introspect to hide the tab bar on child navigation link pages. However, I've noticed some odd behavior when the app is backgrounded and then brought back to the foreground.
It seems like initially, the hidden tab bar is still taking up some space, but this disappears when cycling the app back to the foreground. I'm not sure if this is SwiftUI behavior or has to do with how I'm using Introspect / UIKit.
It's causing layout issues in my app, so I'd like to make the spacing consistent if possible.
Here's a minimal example that shows the behavior:
import SwiftUI
import Introspect
struct ContentView: View {
var body: some View {
TabView {
VStack {
Spacer()
Text("Hello, world!")
}
}
.border(Color.red)
.introspectTabBarController { tabBarController in
tabBarController.tabBar.isHidden = true
}
}
}
Here is the late answer. Basically add tabbar height to current view frame. And onDissappear restore view frame size
import SwiftUI
import Introspect
#State var uiTabarController: UITabBarController?
#State var tabBarFrame: CGRect?
struct ContentView: View {
var body: some View {
TabView {
VStack {
Spacer()
Text("Hello, world!")
}
}
.border(Color.red)
.introspectTabBarController { (UITabBarController) in
uiTabarController = UITabBarController
self.tabBarFrame = uiTabarController?.view.frame
uiTabarController?.tabBar.isHidden = true
uiTabarController?.view.frame = CGRect(x:0, y:0, width:tabBarFrame!.width, height:tabBarFrame!.height+UITabBarController.tabBar.frame.height);
}
.onDisappear {
if let frame = self.tabBarFrame {
self.uiTabarController?.tabBar.isHidden = false
uiTabarController?.view.frame = frame
}
}
}
}

Semi-transparent (blurry like VisualEffectView) of the view behind the current view

In SwiftUI the view behind the tab bar in a TabView will shine through as if the backside of the tab bar was frosted glass. Apple uses this look all over the place in their own apps. But how do I add it to a view in SwiftUI?
Here's an example from the Podcasts app. The tab bar has the frosted glass effect. And so does the overlay mini player on top of the tab bar. Any tab bar in a TabView in will have this look by default, but not an associated overlay (the mini player in this case).
The Apple way
Investigating on the view hierarchy shows that Apple is using UIKit and UIVisualEffectViewfor this reason. You can define a VisualEffectView with just 5 lines of code:
struct VisualEffectView: UIViewRepresentable {
var effect: UIVisualEffect?
func makeUIView(context: UIViewRepresentableContext<Self>) -> UIVisualEffectView { UIVisualEffectView() }
func updateUIView(_ uiView: UIVisualEffectView, context: UIViewRepresentableContext<Self>) { uiView.effect = effect }
}
Usage Example:
struct ContentView: View {
var body: some View {
ZStack {
Image("BG")
.resizable()
.scaledToFill()
.edgesIgnoringSafeArea(.all)
VisualEffectView(effect: UIBlurEffect(style: .dark))
.edgesIgnoringSafeArea(.all)
Text("Hello \nVisual Effect View")
.font(.largeTitle)
.fontWeight(.black)
.foregroundColor(.white)
}
}
}
The Native SwiftUI way:
You can add .blur() modifier on anything you need to be blurry like:
struct ContentView: View {
var body: some View {
ZStack {
Image("BG")
.resizable()
.scaledToFill()
.edgesIgnoringSafeArea(.all)
.blur(radius: 20) // <- this is the important modifier. The rest is just for demo
Text("Hello \nSwiftUI Blur Effect")
.font(.largeTitle)
.fontWeight(.black)
.foregroundColor(.white)
}
}
}
Note the top and bottom of the view
Note that you can Group multiple views and blur them together.
iOS 15 - Apple Material
You can use iOS predefined materials with one line code:
.background(.ultraThinMaterial)

How to change a background color in whole App?

Is there a way to set up the background of the whole app (same default background for each view) in one place? For example in the SceneDelegate?
Create a custom ViewModifier, throw in your color, and add it to your views. For instance, if you want all your views to be orange, do this:
struct BackgroundColorStyle: ViewModifier {
func body(content: Content) -> some View {
return content
.background(Color.orange)
}
}
And usage is:
Text("Hello world!").modifier(BackgroundColorStyle())
Now, you can - and probably should - expand on this for light/dark mode. In this case, you can use the environment variable ColorSchmem:
struct BackgroundColorStyle: ViewModifier {
#Environment (\.colorScheme) var colorScheme:ColorScheme
func body(content: Content) -> some View {
if colorScheme == .light {
return content
.background(Color.darkGrey)
} else {
return content
.background(Color.white)
}
}
}
Either way, every View using this modifier has their background color defined in one place. If you wish to define a border along with a background color, same thing.
import SwiftUI
struct TestView: View {
var body: some View {
ZStack {
Rectangle()
.foregroundColor(.blue)
.edgesIgnoringSafeArea(.all)
Text("Hello World!")
.foregroundColor(.white)
}
}
}
ZStack and Rectangle(), Setting foregroundColor and edgesIgnoringSafeArea

How do I change scrollview's scroll direction in SwiftUI?

I was trying to implement vertical scroll with image and text but I am not able to achieve it.
I tried on both Xcode beta 1 & 2.
Try to wrap both the Text and the Image in a VStack and be sure that there is enough content inside the ScrollView to fall outside it's bounds (in the right direction - vertically in your case):
ScrollView {
VStack {
ForEach (1...100) {_ in
Image(systemName: "circle.fill")
Text("my text")
}
}
}
You could easily try it out in a Playground like this :
import SwiftUI
import PlaygroundSupport
struct LiveView : View {
var body: some View {
ScrollView {
VStack {
ForEach (1...100) {_ in
Image(systemName: "circle.fill")
Text("Some text")
}
}
}
}
}
PlaygroundPage.current.liveView = UIHostingController(rootView: LiveView())