Detect UIImagePicker OverlayView Orientation - ios

Αccording to the Apple's documentation, the image picker only supports portrait mode.
But i want my custom OverlayView to detect device Orientation, for rotate respectively the custom buttons/images i use.
Apple doing that also, on Camera App. If you rotate your device on Landscape, you'll notice that Switch Front Camera Button and Photos thumbnail, rotate itself too.
Is there any way to achieve that?

Well, tried something simple on Controller that uses the imagePicker and did worked.
Let me know, if you have a better solution.
func checkRotation(){
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
print("landscape")
} else {
print("portrait")
}
}
override func viewDidLoad(){
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(self.checkRotation),
name: UIDeviceOrientationDidChangeNotification,
object: nil)
}
EDIT : After struggling with UIDevice.currentDevice().orientation I noticed that randomly didn't received rightly results of orientation. So i followed that one solution posted here on stackoverflow by using CMMotionManager. Just made some changes by using observer and async_patch.
SNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.accelerationGetOrientation), name: UIDeviceOrientationDidChangeNotification, object: nil)
func accelerationGetOrientation(){
uMM = CMMotionManager()
uMM.accelerometerUpdateInterval = 0.2
uMM.startAccelerometerUpdatesToQueue( NSOperationQueue() ) { p, _ in
if p != nil {
if abs( p!.acceleration.y ) < abs( p!.acceleration.x ) {
//LANDSCAPE
dispatch_async(dispatch_get_main_queue()) {
//Do UI Changes
}
}
else {
//PORTRAIT
dispatch_async(dispatch_get_main_queue()) {
//Do UI Changes
}
}
}
}
}

Related

Present login screen with notification center when app enters background and foreground, Swift

I have Login screen and I would like to present it or just add it to navigation, it doesn't matter right now, still not sure which way I would like it to be, everytime my app comes back to foreground. Code below partially works, it does start face id, but actually whole login screen isn't shown, I can still see my last screen that was opened before entering background. I would like to hide everything with login screen. When I print childrens in appMovedToBackground function, correct view controller is printed, which means its added, but as I said, its not showing, only icon from face id can be seen. Code below is on one view controller only, currently, I would like this to be applied to all my view controllers, should I move functionality to appdelegate? Thanks in advance.
let notificationCenter = NotificationCenter.default
override func viewDidLoad() {
super.viewDidLoad()
notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(appCameToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func appMovedToBackground() {
print("its in background")
let authVC = AuthViewController()
navigationController?.topViewController?.addChild(authVC)
}
#objc func appCameToForeground() {
print("app enters foreground")
if let authVC = navigationController?.topViewController?.children.first as? AuthViewController {
print("willEnterForegroundNotification")
authVC.checkBiometricsAndStart()
}
}
Here you need to correctly add the vc
#objc func appMovedToBackground() {
print("its in background")
guard let added = navigationController?.topViewController else { return }
let authVC = AuthViewController()
authVC.view.frame = UIScreen.main.bounds
added.view.addSubview(authVC.view)
added.addChild(authVC)
}
Regarding that you need it for all vcs , yes implementing it inside AppDelegate is the easiest way

iOS Face ID/Touch ID Lock Screen Conflict in willEnterForegroundNotification

I have code that puts a blurred UIVisualEffectView over the current window when the didEnterBackgroundNotification notification is fired. It then tries to reverse this when willEnterForegroundNotification is fired (simplified for easy reading):
class AutoBlur {
init() {
NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
}
#objc func appDidEnterBackground() {
blur()
}
#objc func appWillEnterForeground() {
guard UserState.isScreenLockEnabled else {
unblur()
return
}
let authContext = LAContext()
authContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "test") { success, error in
DispatchQueue.main.async {
guard success else {
return
}
unblur()
}
}
}
//...
}
While this approach generally works, when testing the Face ID functionality of app I found the following anomaly:
Open the app
Turn the screen off on the device
Wake the device
Swipe up on the device's lock-screen
Expected results: The device should unlock, and then my app should run its Face ID evaluate code to un-blur the view.
Actual results: The swipe-up action is undercut by my app's Face ID evaluation code. It prevents the user from being able to swipe up on their lock-screen, effectively locking them out of their device.
Is there some state I should be monitoring or some other event that's safer to respond to for triggering my Face ID evaluation?

Swift 3 device motion gravity

I have an iPhone application with a level in it that is based on the gravityY parameter of a device motion call to motionmanager. I have fixed the level to the pitch of the phone, as I wish to show the user whether the phone is elevated or declined relative to a flat plane (flat to the ground) through its x-axis...side to side or rotated is not relevant. To do that, I have programmed the app to slide an indicator (red when out of level) along the level (a bar)....its maximum travel is each end of the level.
The level works great...and a correct value is displayed, until the user locks the phone and puts it in his or her back pocket. While in this stage, the level indicator shifts to one end of the level (the end of the phone that is elevated in the pocket), and when the phone is pulled out and unlocked, the app does not immediately restore the level - it remains out of level, even if I do a manual function call to restore the level. After about 5 minutes, the level seems to restore itself.
Here is the code:
func getElevation () {
//now get the device orientation - want the gravity value
if self.motionManager.isDeviceMotionAvailable {
self.motionManager.deviceMotionUpdateInterval = 0.05
self.motionManager.startDeviceMotionUpdates(
to: OperationQueue.current!, withHandler: {
deviceMotion, error -> Void in
var gravityValueY:Double = 0
if(error == nil) {
let gravityData = self.motionManager.deviceMotion
let gravityValueYRad = (gravityData!.gravity.y)
gravityValueY = round(180/(.pi) * (gravityValueYRad))
self.Angle.text = "\(String(round(gravityValueY)))"
}
else {
//handle the error
self.Angle.text = "0"
gravityValueY = 0
}
var elevationY = gravityValueY
//limit movement of bubble
if elevationY > 45 {
elevationY = 45
}
else if elevationY < -45 {
elevationY = -45
}
let outofLevel: UIImage? = #imageLiteral(resourceName: "levelBubble-1")
let alignLevel: UIImage? = #imageLiteral(resourceName: "levelBubbleGR-1")
let highElevation:Double = 1.75
let lowElevation:Double = -1.75
if highElevation < elevationY {
self.bubble.image = outofLevel
}
else if elevationY < lowElevation {
self.bubble.image = outofLevel
}
else {
self.bubble.image = alignLevel
}
// Move the bubble on the level
if let bubble = self.bubble {
UIView.animate(withDuration: 1.5, animations: { () -> Void in
bubble.transform = CGAffineTransform(translationX: 0, y: CGFloat(elevationY))
})
}
})
}
}
I would like the level to restore almost immediately (within 2-3 seconds). I have no way to force calibration or an update. This is my first post....help appreciated.
Edit - I have tried setting up a separate application without any animation with the code that follows:
//
import UIKit
import CoreMotion
class ViewController: UIViewController {
let motionManager = CMMotionManager()
#IBOutlet weak var angle: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func startLevel(_ sender: Any) {
startLevel()
}
func startLevel() {
//now get the device orientation - want the gravity value
if self.motionManager.isDeviceMotionAvailable {
self.motionManager.deviceMotionUpdateInterval = 0.1
self.motionManager.startDeviceMotionUpdates(
to: OperationQueue.current!, withHandler: {
deviceMotion, error -> Void in
var gravityValueY:Double = 0
if(error == nil) {
let gravityData = self.motionManager.deviceMotion
let gravityValueYRad = (gravityData!.gravity.y)
gravityValueY = round(180/(.pi) * (gravityValueYRad))
}
else {
//handle the error
gravityValueY = 0
}
self.angle.text = "(\(gravityValueY))"
})}
}
}
Still behaves exactly the same way....
OK....so I figured this out through trial and error. First, I built a stopGravity function as follows:
func stopGravity () {
if self.motionManager.isDeviceMotionAvailable {
self.motionManager.stopDeviceMotionUpdates()
}
}
I found that the level was always set properly if I called that function, for example by moving to a new view, then restarting updates when returning to the original view. When locking the device or clicking the home button, I needed to call the same function, then restart the gravity features on reloading or returning to the view.
To do that I inserted the following in the viewDidLoad()...
NotificationCenter.default.addObserver(self, selector: #selector(stopGravity), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(stopGravity), name: NSNotification.Name.UIApplicationWillTerminate, object: nil)
This notifies the AppDelegate and runs the function. That fixed the issue immediately.

Proximity sensor not working properly after first use

I am building a game with SpriteKit that requires use of the proximity sensor. I have a swipe gesture that turns proximity sensing on, when the proximity sensor senses something it turns off. My problem is that the first time I swipe and put my hand over the sensor, the functions runs and everything works fine. The second time I swipe and put my hand over the sensor the screen just turns black, when I move my hand away from the proximity sensor it then runs the function and everything works. I want it to activate and run the function when I put my hand over the sensor instead of when I remove my hand. Does anybody no how I could fix this?
Proximity sensor code:
func setProximitySensorEnabled(_ enabled: Bool) {
let device = UIDevice.current
device.isProximityMonitoringEnabled = enabled
if device.isProximityMonitoringEnabled == true {
NotificationCenter.default.addObserver(self, selector: #selector(proximityChanged), name: .UIDeviceProximityStateDidChange, object: device)
print("added observer!!!")
} else {
NotificationCenter.default.removeObserver(self, name: .UIDeviceProximityStateDidChange, object: nil)
print("removed observer!!!")
}
}
func proximityChanged(_ notification: Notification) {
if let device = notification.object as? UIDevice {
print("\(device) detected!")
print("An object is near!!")
vibrate()
setProximitySensorEnabled(false)
}
}
Swipe function:
func swipe() {
print("swipe detected")
setProximitySensorEnabled(true)
}
In my case, I found that somewhere in my project set UIDevice.current.isProximityMonitoringEnabled = false after I add the observer. The selector method would not get any notification then.

how to Detect change in Photo asset using ALAssetsLibraryChangedNotification when app is running in background

i used ALAssetsLibraryChangedNotification to detect change in photo asset.
it will work fine but the problem is i need to open the app every time to detect the change. How i can do this in background?
here is my code
override func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "assetChange:", name: ALAssetsLibraryChangedNotification, object: assetsLibrary)
}
func assetChange(notification: NSNotification) {
if var info:NSDictionary = notification.userInfo {
if((info.objectForKey(ALAssetLibraryUpdatedAssetsKey)) != nil){
var set:NSSet = info.objectForKey(ALAssetLibraryUpdatedAssetsKey) as NSSet
}
}
}

Resources