Why taken photo does not appear in SwiftUI? - swiftui

Following this tutorial: https://medium.com/swlh/how-to-open-the-camera-and-photo-library-in-swiftui-9693f9d4586b
But in my case there are two image
struct ContentView: View {
#State private var holeTop: UIImage?
#State private var holeBottom: UIImage?
#State private var selectedImage: UIImage?
When one press button to open taker:
.onTapGesture {
selectedImage = holeTop
camera = true
}.padding(24)
I set selectedImage to one of the image. Is the wrong approach?
And showing the image, if it exist, anyway a placeholder:
.overlay {
holeTop != nil ? Image(uiImage: holeTop!).resizable()
.scaledToFit()
.frame(width: 24, height: 24)
.foregroundColor(.white) :
Image(systemName: "camera")
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
.foregroundColor(.white)
}
So either the photo taker placeholder or the image itself is shown, but unfortunately placeholder will not get updated at all.

Related

How to use scaleEffect with TextEditor in SwiftUI?

I want to use scaleEffect to scale a TextEditor contained in a VStack. When not scaled the text editor is very normal. But After scaling the editor can not be edited normally as the cursor can not be placed in the right place. Here is the code:
import SwiftUI
struct DemoTextView: View {
#State var text: String = ""
#State var scale: Double = 1
var body: some View {
VStack {
Spacer()
VStack {
TextEditor(text: $text)
.frame(width: 100, height: 100, alignment: .top)
.border(Color.red)
}
.scaleEffect(scale)
Spacer()
HStack {
Slider(value: $scale, in: 0.25...4)
Text("\(scale)")
}
.padding()
}
.frame(width: 500, height: 500, alignment: .center)
}
}
struct DemoTextView_Previews: PreviewProvider {
static var previews: some View {
DemoTextView()
}
}
And the screenshot of the wired behavior:
Anyone knows how to make the scaling of TextEditor works?

SwiftUI: VideoPlayer to display different videos stored locally (back and forward buttons)

I'm still new to SwiftUI and have run into a problem. I need to use back and forward buttons to make the Video Player go to the previous/next video (stored locally). The following code works for one video only, the one declared into the init(), but I can't manage to change the video by clicking the back and forward buttons.
I'm using an array of String called videoNames to pass all the video names from the previous View.
Also, I'm using a custom Video Player for this and I'm gonna include the relevant parts of the code.
This is my View:
struct WorkingOutSessionView: View {
let videoNames: [String]
#State var customPlayer : AVPlayer
#State var isplaying = false
#State var showcontrols = false
init(videoNames: [String]) {
self.videoNames = videoNames
self._customPlayer = State(initialValue: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoNames[0], ofType: "mov")!)))
}
var body: some View {
VStack {
CustomVideoPlayer(player: $customPlayer)
.frame(width: 390, height: 219)
.onTapGesture {
self.showcontrols = true
}
GeometryReader {_ in
// BUTTONS
HStack {
// BACK BUTTON
Button(action: {
// code
}, label: {
Image(systemName: "lessthan")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
.foregroundColor((Color(red: 243/255, green: 189/255, blue: 126/255)))
.padding()
})
// FORWARD BUTTON
Button(action: {
// code
}, label: {
Image(systemName: "greaterthan")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
.foregroundColor((Color(red: 243/255, green: 189/255, blue: 126/255)))
.padding()
})
}
}
}
.offset(y: 35)
.edgesIgnoringSafeArea(.all)
}
This is my custom Video Player:
struct CustomVideoPlayer : UIViewControllerRepresentable {
#Binding var player: AVPlayer
func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<CustomVideoPlayer>) {
}
}
I've researched for solutions but couldn't find anything relevant. I've tried to modify my CustomVideoPlayer and not pass a #Binding variable... As well, the init() gave me a lot of headaches as it comes back to errors every time I change something...
Any solution would help guys. I really appreciate your time.
The first thing that you'll need is something to keep track of your position in the video list. I'm using another #State variable for this.
Whenever that state variable changes, you'll need to update your player. I'm using the onChange modifier near the bottom of the code to do this work.
In your CustomVideoPlayer, you need to use updateUIViewController to make sure the player is up-to-date with the parameter being passed in.
Lastly, there's no need for AVPlayer to be a #Binding, since it's a class that is passed by reference, not a struct that is passed by value.
struct WorkingOutSessionView: View {
let videoNames: [String]
#State private var customPlayer : AVPlayer
#State private var currentItem = 0
#State var isplaying = false
#State var showcontrols = false
init(videoNames: [String]) {
self.videoNames = videoNames
self._customPlayer = State(initialValue: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoNames[0], ofType: "mov")!)))
}
var body: some View {
VStack {
CustomVideoPlayer(player: customPlayer)
.frame(width: 390, height: 219)
.onTapGesture {
self.showcontrols = true
}
.onAppear {
self.customPlayer.play()
}
GeometryReader { _ in
// BUTTONS
HStack {
// BACK BUTTON
Button(action: {
currentItem = min(currentItem, currentItem - 1)
}) {
Image(systemName: "lessthan")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
.foregroundColor((Color(red: 243/255, green: 189/255, blue: 126/255)))
.padding()
}
// FORWARD BUTTON
Button(action: {
currentItem = min(videoNames.count - 1, currentItem + 1)
}) {
Image(systemName: "greaterthan")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
.foregroundColor((Color(red: 243/255, green: 189/255, blue: 126/255)))
.padding()
}
}
}
}
.offset(y: 35)
.edgesIgnoringSafeArea(.all)
.onChange(of: currentItem) { currentItem in
print("Going to:",currentItem)
self.customPlayer.pause()
self.customPlayer = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: videoNames[currentItem], ofType: "mov")!))
self.customPlayer.play()
}
}
}
struct CustomVideoPlayer : UIViewControllerRepresentable {
var player: AVPlayer
func makeUIViewController(context: UIViewControllerRepresentableContext<CustomVideoPlayer>) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = player
controller.showsPlaybackControls = false
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<CustomVideoPlayer>) {
uiViewController.player = player
}
}
If this were my own project, I'd probably continue to do some refactoring -- maybe move some things to a view model, etc. Also, I'd probably avoid initializing an AVPlayer in a View's init as I mentioned in my last answer to you on your previous question. It works, but there's definitely a risk you'll end up doing too much heavy lifting if the view hierarchy re-renders.

How to change popup background Color and height in SwiftUi

Firstly,I created a drop-down menu, then I moved it to the part I created for the login screen earlier. However, the drop-down menu opens as shown in the picture.
But I want the drop down menu to open over the text fields below as in the second photo. How can I do that?
struct CountryCodeDropdown: View {
//PROPERTIES
#State private var isExpanded = false
#State private var selectedNum = 1
#State private var isScrollExpanded = false
#State private var selectedScrollNum = 1
var body: some View {
VStack(alignment: .leading){
DisclosureGroup("\(selectedScrollNum)", isExpanded: $isScrollExpanded) {
ScrollView{
VStack{
ForEach(1...50,id:\.self){ code in
Text("\(code)")
.font(Font.custom("Barlow-Regular", size: 12))
.padding(.all)
.onTapGesture {
self.selectedScrollNum = code
withAnimation{
self.isScrollExpanded.toggle()
}
}
}//foreach
}//VSTACK
}.frame( height:50)
}//Disclosure
.accentColor(.black)
.font(Font.custom("Barlow-Regular", size: 17))
.padding(.trailing)
Spacer()
}.frame(height:70)
}
}
Login Page
Then I put the dropdown menu I created on the login screen.
VStack(alignment: .leading, spacing: 3){
HStack{
//CODE
ZStack {
VStack{
CountryCodeDropdown()
.padding(.top,40)
Divider()
.padding(.top,-40)
} .frame(width: 100)
.padding(.leading)
}
//PHONE
CustomTextField(title: "Phone", value: $model.phone, animation: animation)
.keyboardType(.numberPad)
}//:HSTACK
Text("OR")
.padding(.leading)
.padding(.top,4)
.font(Font.custom("Barlow-Regular", size: 12))
CustomTextField(title: "E-mail", value: $model.email, animation: animation)
.keyboardType(.emailAddress)
}//:VSTACK
Everything is working correctly, the only problem is that the height of the ScrollView is too low in CountryCodeDropdown. Use a higher number in .frame(height: 50). something like .frame(height: 250).

Why does putting an SF symbol in a UIImage and then in a SwiftUI Image View result in a blurry image?

I'm trying to write a public init that takes a UIImage, but I want to allow both a normal, actual UIImage made from say a photo (or otherwise drawn) OR use the SF Symbols, but I am stumped as to why the SF Symbols are blurred. What am I missing?
Here is the code I've got:
struct TempUIImageView: View {
var defaultImage: UIImage = UIImage(systemName: "globe")!
var defaultImageString: String = "globe"
var body: some View {
VStack {
Image(uiImage: defaultImage)
.resizable()
.scaledToFill()
.aspectRatio(contentMode: .fit)
Image(systemName: defaultImageString)
.resizable()
.scaledToFill()
.aspectRatio(contentMode: .fit)
}
.padding()
}
}
i'm hoping to use a solution in this:
#State private var displayedImage: UIImage?
...
private var displayImage: some View {
Image(uiImage: displayedImage!)
.resizable()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.scaledToFill()
.aspectRatio(contentMode: .fit)
.clipShape(Circle())
.shadow(radius: 4)
}
But displayedImage could be a UIImage OR an SF Symbol.
(The forced unwrap is dealt with in other code, but this is preliminary code anyways)
When you use Image(systemName:) the system draws to the screen using vector graphics, resulting in sharp, un-blurry renderings.
However, UIImage, including, it seems UIImage(systemName:) is a bitmapped image, at a set resolution. If you scale it up (as you're doing in your second block of code), you'll get blurriness, because you're not getting the benefits of the vector graphics.
Using a pattern like the following would allow you to conditionally display a Image(uiImage:) or Image(systemName:) and still use the same set of modifiers for each:
struct ContentView: View {
#State var displayedImage : UIImage?
var imageToDisplay: Image {
if let displayedImage = displayedImage {
return Image(uiImage: displayedImage)
} else {
return Image(systemName: "camera.circle")
}
}
var body: some View {
imageToDisplay
.resizable()
.scaledToFill()
.aspectRatio(contentMode: .fit)
.frame(width: 400, height: 400)
}
}

How do I animate an image to continuously move across the screen?

This code shows the general behavior I'm trying to achieve, but how can I make this continuous so there's no apparent end to the image, with no gaps of white space and a smooth transition? AND - isn't there a better way to do this???
Thanks!
import SwiftUI
struct ContentView: View {
#State private var xVal: CGFloat = 0.0
#State private var timer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect()
var body: some View {
ZStack {
Image("game_background_2") //image attached
.aspectRatio(contentMode: .fit)
.offset(x: xVal, y: 0)
.transition(.slide)
.padding()
.onReceive(timer) {_ in
xVal += 2
if xVal == 800 { xVal = 0 }
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Here's a simple approach that could work. First use a GeometryReader to set the frame of the screen. Then add an HStack with 2 images inside. The first image is full size and the 2nd image is limited to the width of the screen. Finally, animate the HStack to move from the .trailing to the .leading edge of the screen frame. We set the animation .repeatForever so that it continues loops and autoReverses: false so that it restarts. When it restarts, however, it should be seamless because the restart position is identical to the 2nd image.
struct ImageBackgroundView: View {
#State var animate: Bool = false
let animation: Animation = Animation.linear(duration: 10.0).repeatForever(autoreverses: false)
var body: some View {
GeometryReader { geo in
HStack(spacing: -1) {
Image("game_background_2")
.aspectRatio(contentMode: .fit)
Image("game_background_2")
.aspectRatio(contentMode: .fit)
.frame(width: geo.size.width, alignment: .leading)
}
.frame(width: geo.size.width, height: geo.size.height,
alignment: animate ? .trailing : .leading)
}
.ignoresSafeArea()
.onAppear {
withAnimation(animation) {
animate.toggle()
}
}
}
}