I've added android:windowSoftInputMode="adjustResize" to my AndroidManifest.xml file to allow the textfield, button and text to move up, but the background is also re-sizing itself. How can I stop this from happening?
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
//Used to determine where the starting launch point of the app is
val user by remember { mutableStateOf(Firebase.auth.currentUser) }
AppNavigation(
startDestination = if (user == null) {
ViewToDisplayKeys.SignInOrSignUp.toString()
} else {
ViewToDisplayKeys.OnboardingStart.toString()
}
)
}
}
}
#Composable
fun AppNavigation(
navController: NavHostController = rememberNavController(),
startDestination: String = "signInOrSignUp"
) {
NavHost(
navController = navController,
startDestination = startDestination
) {
composable("signInOrSignUp") {
SignInOrSignUpMasterView(navController = navController)
}
composable("onboarding") {
OnboardingStartView(navController)
}
}
}
Put it in the onCreate method, the code is as follows:
class SignInOrSignUp : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
getWindow().setBackgroundDrawableResource(R.drawable.sign_in_sign_up_background)
setContent {
AppTheme {
Box(modifier = Modifier.fillMaxSize()) {
SignInOrSignUpMasterView()
}
}
}
}
}
#Composable
fun SignInOrSignUpView() {
Box {
//Background image
/*Image(
painter = painterResource(id = R.drawable.sign_in_sign_up_background),
contentDescription = null,
contentScale = ContentScale.FillBounds,
alpha = 0.50F,
modifier = Modifier
.background(Color.White)
)*/
SignUpView()
}
}
You need to set the background to window:
getWindow().setBackgroundDrawableResource(R.drawable.sign_in_sign_up_background)
Related
extension Reactive where Base: UIViewController {
var viewWillAppear: Observable<Void> {
sentMessage(#selector(UIViewController.viewWillAppear(_:)))
.filter { [weak base] _ in
if base?.isImageViewerPresented == true {
return false
}
return true
}
.mapToVoid()
}
}
i want observe viewWillAppear like self.viewWillApplerPublisher
please help how to observe it
extension Reactive where Base: UIViewController {
public var viewWillAppear: ControlEvent<Void> {
let source = base.rx.methodInvoked(#selector(Base.viewWillAppear))
.map { _ in }
return ControlEvent(events: source)
}
}
I want to stop the ongoing screen recording in iOS which user has started using the Control Center. For stopping the recording, the user would click on a button inside Flutter app. Tried using ReplayKit to stop screen recording, as below code:
Environment: Flutter v2.10.5
Challenges faced:
Tested on Simulator and an iPhone, the isScreenRecording bool works, but the stop recording doesn't seem to work.
Any thing wrong here? Or can ReplayKit stop ongoing global recording started from Control Center.
AppDelegate.swift
import UIKit
import Flutter
import MoEngage
import moengage_flutter
import flutter_downloader
import AVFoundation
import Foundation
import ReplayKit
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
}
var moSDKConfig : MOSDKConfig
let moAppId = ""
if let config = MoEngage.sharedInstance().getDefaultSDKConfiguration() {
moSDKConfig = config
moSDKConfig.moeAppID = moAppId
} else {
moSDKConfig = MOSDKConfig.init(appID: moAppId)
}
moSDKConfig.appGroupID = "group.com.package-name.shared"
moSDKConfig.moeDataCenter = DATA_CENTER_01
MOFlutterInitializer.sharedInstance.initializeWithSDKConfig(
moSDKConfig,
andLaunchOptions: launchOptions
)
FlutterDownloaderPlugin.setPluginRegistrantCallback(registerPlugins)
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let isMicrophoneAvailable = FlutterMethodChannel(name: "package-name/audio_recorder",
binaryMessenger: controller.binaryMessenger)
isMicrophoneAvailable.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
guard call.method == "isMicrophoneAvailable" else {
result(FlutterMethodNotImplemented)
return
}
self?.isMicrophoneAvailable(result: result)
})
let screenRecording = FlutterMethodChannel(name: "package-name/screen_recording",
binaryMessenger: controller.binaryMessenger)
screenRecording.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
switch call.method {
case "stop_screen_share", "is_screen_share_active":
self?.screenRecordActions(call, result)
default:
result(FlutterMethodNotImplemented)
}
})
GeneratedPluginRegistrant.register(with: self)
// Set your desired minimumBackgroundFetchInterval
let backgroundIntervalInMinutes = 60
UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*backgroundIntervalInMinutes))
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
private func isMicrophoneAvailable(result: FlutterResult) {
if AVAudioSession.sharedInstance().isOtherAudioPlaying == false {
result(Bool(true))
} else {
result(Bool(false))
}
}
// MARK: - Screen Record
private func screenRecordActions(_ call: FlutterMethodCall, _ result: #escaping FlutterResult) {
switch call.method {
case "stop_screen_share":
RPScreenRecorder.shared().stopCapture( handler: { (error) in
print("stopping recording");
})
case "is_screen_share_active":
let isRecording = RPScreenRecorder.shared().isRecording;
result(isRecording)
default:
print("Not Valid")
}
}
}
private func registerPlugins(registry: FlutterPluginRegistry) {
if (!registry.hasPlugin("FlutterDownloaderPlugin")) {
FlutterDownloaderPlugin.register(with: registry.registrar(forPlugin: "FlutterDownloaderPlugin")!)
}
}
content_protection.dart
part of '../app_extension.dart';
class QContentProtection {
QContentProtection._();
static const platform = MethodChannel('package-name/screen_recording');
final SecureContent _secureContent = SecureContent();
void preventScreenshotAndroid(bool prevent) {
_secureContent.preventScreenshotAndroid(prevent);
}
// Future<void> startRecording() async {
// try {
// await platform.invokeMethod('start_screen_share');
// } catch (e, s) {
// log('[QContentProtection.startRecording] error : $e, $s');
// QAppX.extendedRouter.showNotificationsShade(
// message: e.toString(),
// type: QInAppNotificationType.failure,
// );
// }
// }
Future<void> stopRecording() async {
try {
await platform.invokeMethod('stop_screen_share');
} catch (e, s) {
log('[QContentProtection.stopRecording] error : $e, $s');
QAppX.extendedRouter.showNotificationsShade(
message: e.toString(),
type: QInAppNotificationType.failure,
);
}
}
Future<bool> isScreenRecordingOn() async {
try {
final isScreenRecordingOn = await platform.invokeMethod<bool>('is_screen_share_active');
log('[QContentProtection.isScreenRecordingOn] isScreenRecordingOn : $isScreenRecordingOn');
return isScreenRecordingOn ?? false;
} catch (e, s) {
log('[QContentProtection.isScreenRecordingOn] error : $e, $s');
QAppX.extendedRouter.showNotificationsShade(
message: e.toString(),
type: QInAppNotificationType.failure,
);
return false;
}
}
}
main.dart
onPressed: () {
final isScreenRecordingOn = await QAppX.contentProtection.isScreenRecordingOn();
if (isScreenRecordingOn) {
QAppX.contentProtection.stopRecording();
}
}
hoping someone can help me with this, I am building an iOS app with Swift and SwiftUI that launches different Unity scenes with the click of different buttons. I have the Unity part working well thanks to some good tutorials but I am having trouble being able to change scene with different button clicks. The tutorials I have followed set up the Unity code in a view controller that is then launched from a navigation view. I can launch the view controller and change the sceneNumber to whichever one I want and it will load that scene. I can't find away to do that in a way that is set before I launch the view controller.
Here is my View where the LauncherView has two buttons that will launch the view controller, I would like to change which sceneNumber is sent to the ViewController. I also have the problem of when I go back in the app and click through again nothing is displaying. I think this is because the UnityBridge class is already created and in the background somewhere.
Any help would be greatly appreciated! Thanks.
Here is my view code:
var sceneNumber: String = ""
struct MyViewController: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
let vc = UIViewController()
UnityBridge.getInstance().onReady = {
print("Unity is now ready!")
UnityBridge.getInstance().show(controller: vc)
let api = UnityBridge.getInstance().api
api.test(sceneNumber)
}
return vc
}
func updateUIViewController(_ viewController: UIViewController, context: Context) {}
}
struct ContentView: View {
var body: some View {
VStack{
MyViewController()
}
}
}
struct FirstView: View {
var body: some View {
NavigationView{
VStack{
NavigationLink(destination: ContentView()) { //+ code to set sceneNumber to "1"
Text("Launch Unity Scene 1")
}
Text("")
NavigationLink(destination: ContentView()) { //+code to set sceneNumber to "2"
Text("Launch Unity Scene 2")
}
}
}
}
}
And here is the UnityBridge class code:
import Foundation
import UnityFramework
import SwiftUI
class API: NativeCallsProtocol {
internal weak var bridge: UnityBridge!
/**
Function pointers to static functions declared in Unity
*/
private var testCallback: TestDelegate!
/**
Public API
*/
public func test(_ value: String) {
self.testCallback(value)
}
/**
Internal methods are called by Unity
*/
internal func onUnityStateChange(_ state: String) {
switch (state) {
case "ready":
self.bridge.unityGotReady()
default:
return
}
}
internal func onSetTestDelegate(_ delegate: TestDelegate!) {
self.testCallback = delegate
}
}
class UnityBridge: UIResponder, UIApplicationDelegate, UnityFrameworkListener {
private static var instance : UnityBridge?
internal(set) public var isReady: Bool = false
public var api: API
private let ufw: UnityFramework
public var view: UIView? {
get { return self.ufw.appController()?.rootView }
}
public var onReady: () -> () = {}
public static func getInstance() -> UnityBridge {
print("Hello1.1")
//UnityBridge.instance?.unload()
if UnityBridge.instance == nil {
print("Hello1.2")
UnityBridge.instance = UnityBridge()
}
print("Hello1.3")
return UnityBridge.instance!
}
private static func loadUnityFramework() -> UnityFramework? {
print("Hello2")
let bundlePath: String = Bundle.main.bundlePath + "/Frameworks/UnityFramework.framework"
let bundle = Bundle(path: bundlePath)
if bundle?.isLoaded == false {
bundle?.load()
}
let ufw = bundle?.principalClass?.getInstance()
if ufw?.appController() == nil {
let machineHeader = UnsafeMutablePointer<MachHeader>.allocate(capacity: 1)
machineHeader.pointee = _mh_execute_header
ufw!.setExecuteHeader(machineHeader)
}
return ufw
}
internal override init() {
print("Hello3")
self.ufw = UnityBridge.loadUnityFramework()!
self.ufw.setDataBundleId("com.unity3d.framework")
self.api = API()
super.init()
self.api.bridge = self
self.ufw.register(self)
FrameworkLibAPI.registerAPIforNativeCalls(self.api)
ufw.runEmbedded(withArgc: CommandLine.argc, argv: CommandLine.unsafeArgv, appLaunchOpts: nil)
}
public func show(controller: UIViewController) {
print("Hello4")
if self.isReady {
self.ufw.showUnityWindow()
}
if let view = self.view {
controller.view?.addSubview(view)
}
}
public func unload() {
print("Hello5")
self.ufw.unloadApplication()
}
internal func unityGotReady() {
print("Hello6")
self.isReady = true
onReady()
}
internal func unityDidUnload(_ notification: Notification!) {
print("Hello7")
ufw.unregisterFrameworkListener(self)
UnityBridge.instance = nil
}
}
I already spent a lot of hours trying to figure out a way to change statusBarStyle to light/dark using the new lifecycle SwiftUI App.
The newest posts about the status bar teach how to hide it, but I don't want to do it, I just need to change it to dark or light.
To change the color, the most recent way I found is open SceneDelegate.swift and change window.rootViewController to use my own HostingController, but it will only work for projects using UIKit App Delegate Lifecycle. Using SwiftUI App Lifecycle, the SceneDelegate.swift will not be generated, so where can I do it?
I can do it via General Settings on the Xcode interface. My question is about how to do it via code dynamically.
Target: iOS 14
IDE: Xcode 12 beta 3
OS: MacOS 11 Big Sur
Below is what I got so far.
Everything.swift
import Foundation
import SwiftUI
class LocalStatusBarStyle { // style proxy to be stored in Environment
fileprivate var getter: () -> UIStatusBarStyle = { .default }
fileprivate var setter: (UIStatusBarStyle) -> Void = {_ in}
var currentStyle: UIStatusBarStyle {
get { self.getter() }
set { self.setter(newValue) }
}
}
struct LocalStatusBarStyleKey: EnvironmentKey {
static let defaultValue: LocalStatusBarStyle = LocalStatusBarStyle()
}
extension EnvironmentValues { // Environment key path variable
var localStatusBarStyle: LocalStatusBarStyle {
get {
return self[LocalStatusBarStyleKey.self]
}
}
}
class MyHostingController<Content>: UIHostingController<Content> where Content:View {
private var internalStyle = UIStatusBarStyle.default
#objc override dynamic open var preferredStatusBarStyle: UIStatusBarStyle {
get {
internalStyle
}
set {
internalStyle = newValue
self.setNeedsStatusBarAppearanceUpdate()
}
}
override init(rootView: Content) {
super.init(rootView:rootView)
LocalStatusBarStyleKey.defaultValue.getter = { self.preferredStatusBarStyle }
LocalStatusBarStyleKey.defaultValue.setter = { self.preferredStatusBarStyle = $0 }
}
#objc required dynamic init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
struct TitlePage: View {
#Environment(\.localStatusBarStyle) var statusBarStyle
#State var title: String
var body: some View {
Text(title).onTapGesture {
if self.statusBarStyle.currentStyle == .darkContent {
self.statusBarStyle.currentStyle = .default
self.title = "isDefault"
} else {
self.statusBarStyle.currentStyle = .darkContent
self.title = "isDark"
}
}
}
}
struct ContainerView: View {
var controllers: [MyHostingController<TitlePage>]
init(_ titles: [String]) {
self.controllers = titles.map { MyHostingController(rootView: TitlePage(title: $0)) }
}
var body: some View {
PageViewController(controllers: self.controllers)
}
}
struct PageViewController: UIViewControllerRepresentable {
var controllers: [UIViewController]
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
return pageViewController
}
func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) {
uiViewController.setViewControllers([controllers[0]], direction: .forward, animated: true)
}
typealias UIViewControllerType = UIPageViewController
}
MyApp.swift
import SwiftUI
#main
struct TestAppApp: App {
var body: some Scene {
WindowGroup {
ContainerView(["Subscribe", "Comment"])
}
}
}
struct TestAppApp_Previews: PreviewProvider {
static var previews: some View {
Text("Hello, World!")
}
}
Add two values to the Info.plist:
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
This works with the new SwiftUI App Lifecycle (#main). Verified on iOS14.4.
My suggestion is you just create an AppDelegate Adaptor and do whatever customization you need from there. SwiftUI will handle the creation of AppDelegate and managing its lifetime.
Create an AppDelegate class:
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UIApplication.shared.statusBarStyle = .darkContent
return true
}
}
Now inside your App:
#main
struct myNewApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
Text("I am a New View")
}
}
}
This is not really a solution but the best I could come up with (and ended up doing) was to force app to the dark mode. Either in Info.plist or NavigationView { ... }.preferredColorScheme(.dark)
That will also change the statusBar. You will not be able to change the status bar style per View though.
Before, with SceneDelegate
(code taken from SwiftUI: Set Status Bar Color For a Specific View)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
...
window.rootViewController = MyHostingController(rootView: contentView)
}
After, with no SceneDelegate
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
MyHostingController(rootView: ContentView())
}
}
}
If you follow the answer from the link above and apply it here with the #main, you should be able to achieve your changes.
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
}
}
}
}