Avoiding Keyboard in SwifUI with TextEditor - ios

I'm attempting to recreate a simple version of the iOS notes app. Mind you, I'm a complete Swift novice. My current issue is that I want my view to move up as the keyboard appears. I've implemented some code that does do this, but it has some nasty bugs. It first moves the view up WAY too high, then when you begin typing, the view is where it should be. Here are some photos for representation, as well as my code:
Before the keyboard appears
When the keyboard first appears
Once you begin typing
Code:
class KeyboardResponder: ObservableObject {
#Published var currentHeight: CGFloat = 0
var _center: NotificationCenter
#objc func keyBoardWillShow(notification: Notification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
withAnimation {
currentHeight = keyboardSize.height
print("KEYBOARDSIZE.HEIGHT IN OBSERVER: \(keyboardSize.height)")
}
}
print("KEYBOARD HEIGHT IN OBSERVER: \(currentHeight)")
}
#objc func keyBoardWillHide(notification: Notification) {
withAnimation {
currentHeight = 0
}
}
init(center: NotificationCenter = .default) {
_center = center
_center.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
_center.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
}
First, there is a KeyboardResponder class, listening for keyboard appearances and disappearances (with a published variable for its height).
VStack {
TextEditor(text: $content).padding(.all).foregroundColor(fontColors[self.color]).font(fontStyles[self.font])
HStack {
Spacer()
Button(action: {
self.show = false
}) {
Text("Cancel").foregroundColor(.gray).font(.headline)
}
Spacer()
Button(action: {
self.showPanel = true
}) {
Image(systemName: "textformat").font(.headline).foregroundColor(.white).padding(.all)
}.background(Color.green).clipShape(Circle())
Spacer()
Button(action: {
self.show.toggle()
self.saveData()
}) {
Text("Save").foregroundColor(Color(UIColor.systemBlue)).font(.headline)
}
Spacer()
}
}.padding(.bottom, keyboardResponder.currentHeight)
This is the view, with the editor shown in the photos. At the top of this view I have #ObservedObject var keyboardResponder = KeyboardResponder(). I've tried both .padding(.bottom, keyboardResponder.currentHeight) as well as .offset(y: -keyboardResponder.currentHeight). Anyone know what's going on?

Solution Found:
I finally found a solution that works! I got this code from
https://augmentedcode.io/2020/03/29/revealing-content-behind-keyboard-in-swiftui/
fileprivate final class KeyboardObserver: ObservableObject {
struct Info {
let curve: UIView.AnimationCurve
let duration: TimeInterval
let endFrame: CGRect
}
private var observers = [NSObjectProtocol]()
init() {
let handler: (Notification) -> Void = { [weak self] notification in
self?.keyboardInfo = Info(notification: notification)
}
let names: [Notification.Name] = [
UIResponder.keyboardWillShowNotification,
UIResponder.keyboardWillHideNotification,
UIResponder.keyboardWillChangeFrameNotification
]
observers = names.map({ name in
NotificationCenter.default.addObserver(forName: name,
object: nil,
queue: .main,
using: handler)
})
}
#Published var keyboardInfo = Info(curve: .linear, duration: 0, endFrame: .zero)
}
fileprivate extension KeyboardObserver.Info {
init(notification: Notification) {
guard let userInfo = notification.userInfo else { fatalError() }
curve = {
let rawValue = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! Int
return UIView.AnimationCurve(rawValue: rawValue)!
}()
duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! TimeInterval
endFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
}
}
struct KeyboardVisibility: ViewModifier {
#ObservedObject fileprivate var keyboardObserver = KeyboardObserver()
func body(content: Content) -> some View {
GeometryReader { geometry in
withAnimation() {
content.padding(.bottom, max(0, self.keyboardObserver.keyboardInfo.endFrame.height - geometry.safeAreaInsets.bottom))
.animation(Animation(keyboardInfo: self.keyboardObserver.keyboardInfo))
}
}
}
}
fileprivate extension Animation {
init(keyboardInfo: KeyboardObserver.Info) {
switch keyboardInfo.curve {
case .easeInOut:
self = .easeInOut(duration: keyboardInfo.duration)
case .easeIn:
self = .easeIn(duration: keyboardInfo.duration)
case .easeOut:
self = .easeOut(duration: keyboardInfo.duration)
case .linear:
self = .linear(duration: keyboardInfo.duration)
#unknown default:
self = .easeInOut(duration: keyboardInfo.duration)
}
}
}
extension View {
func keyboardVisibility() -> some View {
return modifier(KeyboardVisibility())
}
}
Then just add the modifier to the view you want to move up with the keyboard like so .keyboardVisibility()

add this line in the VStack where you add bottom padding, cause that vStack take padding from the safe area( after keyboard appear the key board area is also belongs to safe area)
.edgesIgnoringSafeArea(.bottom)

Related

How to open view on notification tapped using SwiftUI with proper hierarchy of views and go back to main application

How can I go to OrderReviewView with ContentViewz on navigation stack hierarchy? So navigation stack looks like ContentView -> OrderReviewView ?
At this moment I managed to open the view, but not in the way I expected. I managed to open the view in several ways, the first of which was using the internal notification NotificationCenter.default.publisher(for: NSNotification.Name("OrderReviewView")) . After receiving it in the ContentView class, I set the variable bool showReviewOrder. The view was opening but I couldn't roll back from the view to the Main class. Another way is presented below, the view also opens, but as in the first case, I can't go back to the main application class. I tried to add NavigationStack by changing the line let host = UIHostingController(rootView: NavigationStack {orderWriteScreen.navigationBarBackButtonHidden(true))} and NavigationLink to the OrderReviewScreen and structure as follow
NavigationStack {
ZStack {
VStack(alignment: .leading){
Text("Test")
}
NavigationLink(destination: ContentView())
{FAB() }
.padding(EdgeInsets(top: 0, leading: 0, bottom: 50, trailing: 20))
.frame(maxHeight: .infinity, alignment: .bottom)
.frame(maxWidth: .infinity, alignment: .trailing)
}
}
I was able to go back to the main application class, but there was a back button at the top of the screen and the hierarchy was still one window too many. I am attaching minimal working example of my code to show the problem.
#main
struct App: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
NavigationStack {
if authManager.authorized == nil {
SignInScreen()
} else {
Main()
}
}
}
}
struct Main: View {
var body: some View {
TabBar()
}
}
extension TabBar {
var baseTabBar: some View {
TabView(selection: $selection) {
OrderScreen()
.tag(Tab.contracts)
}
.padding(.bottom, _displayTabBar ? tabBarHeight : 0)
}
}
struct TabBar: View {
let tabBarHeight: CGFloat = 55
#State private var selection = Tab.farms
#State var _displayTabBar = true
init() {
UITabBar.appearance().isHidden = true
}
var body: some View {
ZStack(alignment: .bottom) {
baseTabBar
.environment(\.displayTabBar, displayTabBar)
if (_displayTabBar) {
styledTabBar
}
}
}
}
struct OrderScreen: View {
var body: some View {
ZStack {
VStack(alignment: .leading) {
}
}
}
struct OrderReviewScreen: View {
var body: some View {
VStack(alignment: .leading) {
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
FirebaseConfiguration.shared.setLoggerLevel(.debug)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions) { _, _ in }
application.registerForRemoteNotifications()
Messaging.messaging().delegate = self
return true
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Messaging.messaging().apnsToken = deviceToken
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void
) {
let userInfo = response.notification.request.content.userInfo
let info = userInfo as NSDictionary
guard let screen = info.value(forKey: "screen") as? String else { return }
guard let id = info.value(forKey: "id") as? String else { return }
notificationManager?.notifyId = id
notificationManager?.notifyScreen = screen
if screen == "order_details" {
let orderWriteScreen = OrderReviewScreen()
let host = UIHostingController(rootView: orderWriteScreen.navigationBarBackButtonHidden(true))
UIApplication.shared.windows.first?.rootViewController = host
}
completionHandler()
}
}
}

How present view over any view in swiftui

I'm implementing a chat application and I have an incoming call
so I have presented an incoming call screen when user Accept I have implemented by the notification center
public extension Notification.Name {
static let incomingCallView = Notification.Name("incomingCallView")
}
struct ModalData {
let onDismiss: (() -> Void)?
let content: (Binding<Bool>) -> AnyView
init<Content: View>(onDismiss: (() -> Void)? = nil,
#ViewBuilder content: #escaping (Binding<Bool>) -> Content) {
self.onDismiss = onDismiss
self.content = { AnyView(content($0)) }
}
static let empty = ModalData { _ in EmptyView() }
}
and this in provider delegate
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
guard let call = callManager.callWithUUID(uuid: action.callUUID) else {
action.fail()
return
}
callOption.ringingPeriod?.invalidate()
callOption.ringingPeriod = nil
socket.inizalizeWebrtc(webRTCClient: WebRTCClient(iceServers: Config.default.webRTCIceServers))
socket.webRTCClient?.offer {(sdp) in
// self.hasLocalSdp = true
self.socket.sendSdp(sdp: sdp, type: "offer", callType: self.callOption.callType == .videoCall
? "video"
: "audio"
)
}
call.answer()
NotificationCenter.default.post(name: .incomingCallView,
object: ModalData(onDismiss: {
}) { isPresented in
IncomingCallScreen(callScreenIsPresented: isPresented)
})
action.fulfill()
}
the problem that I have to add .onReceive in all views to open the incoming call screen if I didn't add .onrecieve it will not present
.onReceive(NotificationCenter.default.publisher(for: .incomingCallView)) { notif in
if let data = notif.object as? ModalData {
modalData = data
incomingCallView = true
}
}
.fullScreenCover(isPresented: $incomingCallView,
onDismiss: modalData.onDismiss) {
modalData.content($incomingCallView)
}
How open it without adding .onReceive in all views is there another solution

How to get a view to update automatically after period of inactivity?

I am trying to add a timeout feature to my SwiftUI app. The view should be updated when timeout is reached. I have found code on a different thread, which works for the timeout part, but I cannot get the view to update.
I am using a static property in the UIApplication extension to toggle the timeout flag. Looks like the view is not notified when this static property changes. What is the correct way to do this?
Clarification added:
#workingdog has proposed an answer below. This does not quite work, because in the actual app, there is not just one view, but multiple views that the user can navigate between. So, I am looking for a global timer that gets reset by any touch action whatever the current view is.
In the sample code, the global timer works, but the view does not take notice when the static var UIApplication.timeout is changed to true.
How can I get the view to update? Is there something more appropriate for this purpose than a static var? Or maybe the timer should not be in the UIApplication extension to begin with?
Here is my code:
import SwiftUI
#main
struct TimeoutApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
}
}
}
extension UIApplication {
private static var timerToDetectInactivity: Timer?
static var timeout = false
func addTapGestureRecognizer() {
guard let window = windows.first else { return }
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapped))
tapGesture.requiresExclusiveTouchType = false
tapGesture.cancelsTouchesInView = false
tapGesture.delegate = self
window.addGestureRecognizer(tapGesture)
}
private func resetTimer() {
let showScreenSaverInSeconds: TimeInterval = 5
if let timerToDetectInactivity = UIApplication.timerToDetectInactivity {
timerToDetectInactivity.invalidate()
}
UIApplication.timerToDetectInactivity = Timer.scheduledTimer(
timeInterval: showScreenSaverInSeconds,
target: self,
selector: #selector(timeout),
userInfo: nil,
repeats: false
)
}
#objc func timeout() {
print("Timeout")
Self.timeout = true
}
#objc func tapped(_ sender: UITapGestureRecognizer) {
if !Self.timeout {
print("Tapped")
self.resetTimer()
}
}
}
extension UIApplication: UIGestureRecognizerDelegate {
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text(UIApplication.timeout ? "TimeOut" : "Hello World!")
.padding()
Button("Print to Console") {
print(UIApplication.timeout ? "Timeout reached, why is view not updated?" : "Hello World!")
}
}
}
}
to update the view when timeout is reached, you could do something like this:
import SwiftUI
#main
struct TimeoutApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
#State var thingToUpdate = "tap the screen to rest the timer"
#State var timeRemaining = 5
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
VStack (spacing: 30) {
Text("\(thingToUpdate)")
Text("\(timeRemaining)")
.onReceive(timer) { _ in
if timeRemaining > 0 {
timeRemaining -= 1
} else {
thingToUpdate = "refreshed, tap the screen to rest the timer"
}
}
}
.frame(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity,
minHeight: 0, idealHeight: .infinity, maxHeight: .infinity,
alignment: .center)
.contentShape(Rectangle())
.onTapGesture {
thingToUpdate = "tap the screen to rest the timer"
timeRemaining = 5
}
}
}
You can do this like this - >
Create a subclass of "UIApplication" (use separate files like
MYApplication.swift).
Use the below code in the file.
Now method "idle_timer_exceeded" will get called once the user stops
touching the screen.
Use notification to update the UI
import UIKit
import Foundation
private let g_secs = 5.0 // Set desired time
class MYApplication: UIApplication
{
var idle_timer : dispatch_cancelable_closure?
override init()
{
super.init()
reset_idle_timer()
}
override func sendEvent( event: UIEvent )
{
super.sendEvent( event )
if let all_touches = event.allTouches() {
if ( all_touches.count > 0 ) {
let phase = (all_touches.anyObject() as UITouch).phase
if phase == UITouchPhase.Began {
reset_idle_timer()
}
}
}
}
private func reset_idle_timer()
{
cancel_delay( idle_timer )
idle_timer = delay( g_secs ) { self.idle_timer_exceeded() }
}
func idle_timer_exceeded()
{
// You can broadcast notification here and use an observer to trigger the UI update function
reset_idle_timer()
}
}
You can view the source for this post here.
Here is my code for a "global" timer using the "Environment". Although it works for me, it seems to be a lot of
work just to do something simple. This leads me to believe there must be
a better way to do this. Anyhow, there maybe some ideas you can recycle here.
import SwiftUI
#main
struct TestApp: App {
#StateObject var globalTimer = MyGlobalTimer()
var body: some Scene {
WindowGroup {
ContentView().environment(\.globalTimerKey, globalTimer)
}
}
}
struct MyGlobalTimerKey: EnvironmentKey {
static let defaultValue = MyGlobalTimer()
}
extension EnvironmentValues {
var globalTimerKey: MyGlobalTimer {
get { return self[MyGlobalTimerKey] }
set { self[MyGlobalTimerKey] = newValue }
}
}
class MyGlobalTimer: ObservableObject {
#Published var timeRemaining: Int = 5
var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
func reset(_ newValue: Int = 5) {
timeRemaining = newValue
}
}
struct ContentView: View {
#Environment(\.globalTimerKey) var globalTimer
#State var refresh = true
var body: some View {
GlobalTimerView { // <-- this puts the content into a tapable view
// the content
VStack {
Text(String(globalTimer.timeRemaining))
.accentColor(refresh ? .black :.black) // <-- trick to refresh the view
.onReceive(globalTimer.timer) { _ in
if globalTimer.timeRemaining > 0 {
globalTimer.timeRemaining -= 1
refresh.toggle() // <-- trick to refresh the view
print("----> time remaining: \(globalTimer.timeRemaining)")
} else {
// do something at every time step after the countdown
print("----> do something ")
}
}
}
}
}
}
struct GlobalTimerView<Content: View>: View {
#Environment(\.globalTimerKey) var globalTimer
let content: Content
init(#ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
ZStack {
content
Color(white: 1.0, opacity: 0.001)
.frame(minWidth: 0, idealWidth: .infinity, maxWidth: .infinity,
minHeight: 0, idealHeight: .infinity, maxHeight: .infinity,
alignment: .center)
.contentShape(Rectangle())
.onTapGesture {
globalTimer.reset()
}
}.onAppear {
globalTimer.reset()
}
}
}

SwiftUI TextEditor Scroll with Show Keyboard Not Working

I have a SwiftUI application with SwiftUI lifecycle that uses TextFields and
TextEditors. I have written a modifier to deal with scrolling the content that would
be underneath the keyboard when the keyboard is present. My modifier works as expected
for TextFields but not at all for TextEditors. The Apple docs say the
keyboardWillShowNotification is
posted immediately prior to the display of the keyboard. I cannot find anything which
states the notification is limited to only TextFields. Both field types are in the same ScrollView. Hopefully I'm missing
something simple here.
My modifier:
struct AdaptsToSoftwareKeyboard: ViewModifier {
#State var currentHeight: CGFloat = 0
func body(content: Content) -> some View {
content
.padding(.bottom, self.currentHeight)
.edgesIgnoringSafeArea(self.currentHeight == 0 ? Edge.Set() : .bottom)
.onAppear(perform: subscribeToKeyboardEvents)
}
private let keyboardWillOpen = NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.map { $0.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect }
.map { $0.height }
private let keyboardWillHide = NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map { _ in CGFloat.zero }
private func subscribeToKeyboardEvents() {
_ = Publishers.Merge(keyboardWillOpen, keyboardWillHide)
.subscribe(on: RunLoop.main)
.assign(to: \.self.currentHeight, on: self)
}
}
Any guidance would be appreciated. Xcode Version 12.2 beta (12B5018i) iOS 14
First edit: I thought the issue was likely a failure to receive notifications when the TextEditor receives the focus. However, adding this modifier to the TextEditor shows that the notification is fired. No progress:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.keyboardWillShowNotification)) { _ in
print("Cursor is in TextEditor!")
}
I assume you should store subscriber as below
struct AdaptsToSoftwareKeyboard: ViewModifier {
#State var currentHeight: CGFloat = 0
#State private var cancelable: AnyCancellable?
func body(content: Content) -> some View {
content
.padding(.bottom, self.currentHeight)
.edgesIgnoringSafeArea(self.currentHeight == 0 ? Edge.Set() : .bottom)
.onAppear(perform: subscribeToKeyboardEvents)
}
private let keyboardWillOpen = NotificationCenter.default
.publisher(for: UIResponder.keyboardWillShowNotification)
.map { $0.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect }
.map { $0.height }
private let keyboardWillHide = NotificationCenter.default
.publisher(for: UIResponder.keyboardWillHideNotification)
.map { _ in CGFloat.zero }
private func subscribeToKeyboardEvents() {
self.cancelable = Publishers.Merge(keyboardWillOpen, keyboardWillHide)
.subscribe(on: RunLoop.main)
.assign(to: \.self.currentHeight, on: self)
}
}

ReplayKit with SwiftUI

I want to record the screen with ReplayKit.
I have researched the method with UIKit. But my project used SwiftUI, so I want to use the ReplayKit to record the screen with SwiftUI.
How I record the screen with SwiftUI?
-
When I use the stopRecording function, the function will have previewViewController. But I cannot call present function to present previewViewController.
Note: This is not a very practical answer.
In most cases, "SwiftUI.View" runs on top of "UIHostingController".
You need to grab this to present the "RPPreviewViewController".
You can find one of them by following the "UIApplication".
let scene = UIApplication.shared.connectedScenes.first as! UIWindowScene
let viewController = scene.windows.last!.rootViewController
viewController.present(previewViewController, animated: true, completion:nil)
I have just open-sourced a simple ReplayKit application, which uses SwiftUI.
https://github.com/snakajima/ReplayStartUpKit
Please take a look at how it presents a RPBroadcastActivityViewController from SwiftUI.
It first stores the pointer to the controller in a property bavController, then set #Pubilshed property activePopup to initiate SwiftUI change.
RPBroadcastActivityViewController.load { controller, error in
if let controller = controller {
self.bavController = controller
controller.delegate = self
self.activePopup = .broadCast
}
}
In SwiftUI (MainUIView.swift), the following view is activated when the property activePopup becomes .broadCast.
.sheet(item: $state.activePopup) { item in
switch(item) {
case .broadCast:
BroadcastActivityController(controller: state.bavController!)
}
}
BroadcastActivityController is bit long because of a work-around for iPad, but it is just a wrapper of RPBroadcastActivityController.
struct BroadcastActivityController: UIViewControllerRepresentable {
let controller: RPBroadcastActivityViewController
func makeUIViewController(context: Context) -> RPBroadcastActivityViewController {
return controller
}
func updateUIViewController(_ uiViewController: RPBroadcastActivityViewController, context: Context) {
// Hack to work around iPad issue
if UIDevice.current.userInterfaceIdiom == .pad {
guard let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate,
let vc = sceneDelegate.uiWindow?.rootViewController,
let view = vc.view else {
print("somethign is really wrong")
return
}
controller.modalPresentationStyle = .popover
if let popover = controller.popoverPresentationController {
popover.sourceRect = CGRect(origin: .zero, size: CGSize(width: 10, height: 10))
popover.sourceView = view
popover.permittedArrowDirections = []
}
}
}
typealias UIViewControllerType = RPBroadcastActivityViewController
}
You need to do something very similar to previewViewController.
Try This
import SwiftUI
import ReplayKit
struct ContentView: View {
let recorder = RPScreenRecorder.shared()
#State var isBool = false
#State var rp: RPPreviewView!
#State var isRecording = false
#State var isShowPreviewVideo = false
var body: some View {
ZStack {
VStack {
Button(action: {
if !self.isRecording {
self.startRecord()
} else {
self.stopRecord()
}
}) {
Image(systemName: isRecording ? "stop.circle" : "video.circle")
.resizable()
.frame(width: 100, height: 100)
}
}
if isShowPreviewVideo {
rp
.transition(.move(edge: .bottom))
.edgesIgnoringSafeArea(.all)
}
}
}
func startRecord() {
guard recorder.isAvailable else {
print("Recording is not available at this time.")
return
}
if !recorder.isRecording {
recorder.startRecording { (error) in
guard error == nil else {
print("There was an error starting the recording.")
return
}
print("Started Recording Successfully")
self.isRecording = true
}
}
}
func stopRecord() {
recorder.stopRecording { (preview, error) in
print("Stopped recording")
self.isRecording = false
guard let preview = preview else {
print("Preview controller is not available.")
return
}
self.rp = RPPreviewView(rpPreviewViewController: preview, isShow: self.$isShowPreviewVideo)
withAnimation {
self.isShowPreviewVideo = true
}
}
}
}
struct RPPreviewView: UIViewControllerRepresentable {
let rpPreviewViewController: RPPreviewViewController
#Binding var isShow: Bool
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> RPPreviewViewController {
rpPreviewViewController.previewControllerDelegate = context.coordinator
rpPreviewViewController.modalPresentationStyle = .fullScreen
return rpPreviewViewController
}
func updateUIViewController(_ uiViewController: RPPreviewViewController, context: Context) { }
class Coordinator: NSObject, RPPreviewViewControllerDelegate {
var parent: RPPreviewView
init(_ parent: RPPreviewView) {
self.parent = parent
}
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
withAnimation {
parent.isShow = false
}
}
}
}

Resources