I have an issue with CoreMotion and the following code :
let motionManager = CMMotionManager()
It blocks my Mainthread for 4-5 seconds and I don't know why. The problem appears when I updated my iPhone XR to 12.2. It does not block the mainthread with a iPhone 6S on 12.1.3.
I think it may be a hardware problem or iOS version.
Thank you
CoreMotion is doing a lot on his own during init.
Move the initialisation do a different thread.
Edit:
I can confirm the issue with 12.2 on a development iPhone Xs. No issue on a real used device. I see also violation warnings telling CoreMotion tries to access the Applicationstate from a background thread.
However moving the init to a separate thread fixes any UI hangs here. The init of coremotion still does take a while. I created a empty single view application project and changed the ViewController class
class ViewController: UIViewController {
var motionManager: CMMotionManager?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
view.backgroundColor = UIColor.red
DispatchQueue.global().async {
self.motionManager = CMMotionManager()
DispatchQueue.main.async {
self.view.backgroundColor = UIColor.green
}
}
view.backgroundColor = UIColor.yellow
}
}
Without a separate thread, the red color remains. With a separate thread the color is instant yellow and finally green on the dev XS and instant green on my iPhone 8Plus.
Addition:
Interestingly, without XCode attached, the dev device has no issues.
Try to run your code without being connected to the Debugger.
Related
In iOS12 and below I used to use something similar to this to show a window on top of everything to cover my app contents. This use to work but in iOS13 betas this does not work anymore.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var coverWindow: UIWindow?
func applicationDidEnterBackground(_ application: UIApplication) {
if self.coverWindow != nil {
// Skip since cover window is already showing
return
}
let vc = UIViewController()
let label = UILabel(frame: window!.bounds)
label.text = "CoverWindow. Tap to app see contents"
vc.view = label
vc.view.backgroundColor = UIColor.lightGray
let coverWindow = UIWindow(frame: window!.bounds)
coverWindow.rootViewController = vc
coverWindow.windowLevel = .alert
coverWindow.makeKeyAndVisible()
self.coverWindow = coverWindow
}
}
Apparently window changes are not reflected in screen until app enters foreground again.
Question
Does anyone know how fix or workaround this? or maybe this approach is incorrect?
Any help would be highly appreciated
Notes
I don't use a simple view because my app might be showing other windows too and my requirement is to cover everything.
I don't use applicationWillResignActive because we want to only show coverWindow when it enters background. (TouchID authentication and other stuff might trigger applicationWillResignActive and coverWindow would incorrectly show)
Example code
Download Full working example code in Github (Run in iOS simulator 12 and 13 to see the difference)
You have to implement application life cycle, you just delete it , add those app life cycle functions and implement your codes , it ll be run without error
Answer to myself.
I reported this to Apple and it was fixed in iOS 13.1 or so. Latest version of iOS13 does NOT have this bug :)
The following is an extremely simplified version of the issue:
import UIKit
import CoreMotion
class ViewController: UIViewController {
private let manager = CMMotionManager()
private let interval = 0.01
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
startUpdates()
}
private func startUpdates() {
let queue = OperationQueue()
queue.name = "com.demo.motion"
queue.qualityOfService = .userInteractive
queue.maxConcurrentOperationCount = 1
manager.accelerometerUpdateInterval = interval
manager.startAccelerometerUpdates(to: queue) { _, _ in }
}
}
When I run this on an iPhone XR, I will receive the following:
Main Thread Checker: UI API called on a background thread:
-[UIApplication applicationState] PID: 1237, TID: 147763, Thread name: com.apple.CoreMotion.MotionThread, Queue name:
com.apple.root.default-qos.overcommit, QoS: 0
My device is running 12.3, which is currently the latest iOS release
Running this on various iPads (pre 2018), iPhone 8, iPhone 6S, and other older models, the issue does not appear.
Changing the qualityOfService has no impact on this (my preference is to use utility)
Out of curiosity, I wrapped the entire startUpdates() function around a main dispatch with no success
Based on the queue name and when this is occurring, my assumption is that this is something internal that is just getting exposed by the faster processor in the XR, versus the older devices.
New answer when using iOS 13
I have tested this just now with the newly released iOS 13 version (17A577) on a 2018 device and it no longer produces the threading error.
Previous answer when using iOS 12
This is a known issue that has been reported to Apple with a radar linked here.
This question has been changed due to ongoing debugging. Initially the indicators were that it was iOS8.4x vs iOS9.2 on the target issue. Upon more debugging I now believe may be an issue about touch sensitivity.
I have a refresh button in UIToolBar that I disable and grey-out while refreshing as a signifier for a somewhat long refresh operation.
The code below:
was built using Xcode 7.2/iOS 9.2
works on iOS 9.x simulators for iPad2 and iPhone 5.
does not work (the button works, executes refresh logic but does not grey out during operation) on iOS8.x iPad2 and iPhone5 devices. However it works on iPhone4 iOS 8.4 devices if the button is pressed for at least 2 seconds. On a fleeting touch the method executes, but the disable action and its visual effect (grey out) do not happen.
Hypothesis: It works a simulator since it simulates a touch event through mouse clicks.
Image below shows the correct behavior during refresh on iOS9.2 simulator
Image below shows that refresh button is not greyed out during refresh on iOS 8.4 device when touched for < 1s. The target method executes, but its disable action has no effect.
As a side-note, the iPhone5 does not have SIM but is on WiFi and network functionality of the app is correct.
Do I need to do something different so that a disabled button greys-out in iOS8.x independent of touch duration? In the code, I have comments about what I have tried and did not work. Speculation: Is it possible that some artifacts of 3D touch in iOS9.2 SDK have slipped into the application binary and making it misbehave on 8.4x phone target?
import UIKit
import QuartzCore
class MyViewController: UIViewController {
let refreshButton = UIBarButtonItem(image: UIImage(named:"refresh"),landscapeImagePhone: UIImage(named:"refresh"), style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
//..other buttons.
//..... Other code that has nothing to do with refreshButton
override func viewWillAppear(animated: Bool) {
refreshButton.addTargetForAction(self, action: "performRefresh:")
let space1 = UIBarButtonItem(barButtonSystemItem:UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
buttonArray = [someButton, space1, refreshButton] // There are more buttons.
self.setToolbarItems(buttonArray, animated: false)
} //viewWillAppear
func performRefresh(sender: UIButton) {
NSLog("************Refresh Requested")
//sender.enabled = false //compiles, OK iOS 9, but does not grey out button on iOS8
//sender.anyFunction() // Results in exception stating that sender does not know that message.
//Switching to self.refreshButton instead.
self.refreshButton.enabled = false // OK iOS 9, but does not grey out button on iOS8
//self.refreshButton.tintColor = UIColor.blueColor // has not effect in either OS
//self.refreshButton.alpha or self.refreshButton.setNeedsDisplay() are not present.
//Refresh Logic
NSLog("************Refresh Done")
self.refreshButton.enabled = true
//I had the reciprocal operations (restore tint, set alpha to 1.0 here while debugging)
} //performRefresh
} // myViewController
I might not be understanding your question so well but maybe you can just change the buttons tint color when its disabled self.refreshButton.tintColor = [UIColor lightGrayColor]; and then change the color back when its done.
After app launched for a long time,
there are some logs in console while touching the screen:
BKSendHIDEvent: IOHIDEventSystemConnectionDispatchEvent error:0xE00002E8 -- Unknown event dropped
and all buttons have no response, whole app freeze.
Currently, this problem only happened on iPhone 5s.
Similar issue: https://forums.xamarin.com/discussion/55646/alot-of-annotation-on-mkmapview
Does anyone have the same issue?
Update: I've found that there are more than 500 threads when app being killed by iOS, because I use a third party class Reachability too many times. To fix that, I declare a static variable, the freeze seems not happen again.
static Reachability *staticReachability;
+(NetworkStatus)detectNetwork{
if (staticReachability == nil) {
staticReachability = [Reachability reachabilityForInternetConnection];
[staticReachability startNotifier];
}
NetworkStatus status = [staticReachability currentReachabilityStatus];
return status;
}
I have the same problem.
In My case it happends after switch off wifi in iPad (OS 9.1). Application is unresponsive. In console I can see the same errors.
After switch wifi on, application is again responsive and I can continue.
I have got fix for that...
In My case using swift, with Xcode 7.2.
I have used custom label, and func layoutSubviews() call infinite time and console is showing BKSendHIDEvent: IOHIDEventSystemConnectionDispatchEvent
below is fix for same:
class CustomLabel: UILabel {
var isSubLayoutSet: Bool = false
override internal init(frame: CGRect) {
super.init(frame: frame)
}
required internal init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
if isSubLayoutSet == false
{
//--- do your stuff related to set font or any operation...
///---
isSubLayoutSet = true
}
}
fyi, i'm seeing the same console output "unknown event dropped" after updating to iOS 9.3. I'm not sure if it's the OS, or a specific app running a background process, but I see it in many different apps including the home screen as well as immediately on restarting, so I think it's a bug in the latest 9.3 update.
I am having a problem with the proximity sensor on the iPhone. It seems that when the idle timer is disabled and the proximity monitoring is enabled the screen (sometimes) will turn on when receiving a notification. After this the proximity state is reported incorrectly and the screen won't turn off again.
I provided a sample project to illustrate the problem. But the steps to reproduce are quite simple. I tested this on multiple phones (4S, 5, 6, 6+) with multiple iOS versions (7.0.3 and 8.3). It seems to occur most reliably when the phone is not connected to a power source or debugger.
The code in my only ViewController is (the view is created in a storyboard):
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var checkingToggleButton: UIButton!
#IBOutlet weak var debugLabel: UILabel!
#IBOutlet weak var screenDebugLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.checkingToggleButton.setTitle("Start checking", forState: UIControlState.Normal)
self.debugLabel.text = "Not checking"
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("proximityChanged:"), name: "UIDeviceProximityStateDidChangeNotification", object: nil)
}
#IBAction func handleCheckingToggle(sender: AnyObject) {
let enabled = !UIDevice.currentDevice().proximityMonitoringEnabled
if enabled {
self.debugLabel.text = "Checking"
self.checkingToggleButton.setTitle("Stop checking", forState: UIControlState.Normal)
} else {
self.debugLabel.text = "Not checking"
self.checkingToggleButton.setTitle("Start checking", forState: UIControlState.Normal)
}
UIApplication.sharedApplication().idleTimerDisabled = enabled
UIDevice.currentDevice().proximityMonitoringEnabled = enabled
}
func proximityChanged(notification:NSNotification)
{
if UIDevice.currentDevice().proximityState{
self.screenDebugLabel.text = "Proximity true"
println("Proximity true")
} else {
self.screenDebugLabel.text = "Proximity false"
println("Proximity false")
}
}
}
Steps to reproduce:
Build the provided sample project on a device and run the App on the the device disconnected from power and the debugger (this seems to make some difference).
Enable proximityMonitoring and disable the idle timer by pressing the "Start checking" button.
Cover the proximity sensor, this turns the screen off.
Wait approximately 2 minutes (this also makes a difference, doing it too soon won't reproduce the problem)
Send yourself a notification (for instance an iMessage)
The screen will turn on and won't turn off again. We also created a label that shows the current proximityState, the state is (incorrectly) reported as false.
Link to sample project on GitHub:
https://github.com/TimPelgrim/ProximityTest
I've tested a lot of things with the Notification method.
As Asi mentioned it, it doesn't work well. The solution is to observe the variable.
The issue is still there in iOS 11.4.1 (not tested on iOS 12 beta yet).
Swift 4:
// Prepare you observer variable
private var observer: NSKeyValueObservation?
// ...
// Start observing proximityState
self.observer = UIDevice.current.observe(\.proximityState) { [weak self] (device, _) in
print("State changed: \("device.proximityState")")
}
// ...
// Don't forget to call the `invalidate` method on your observer when you want to stop observing
self.observer?.invalidate()
I sent in a technical support issue to the apple engineers and they told me this is a bug in the operating system. No report on when it gets fixed but be advised that this issue seems to be occur in all versions of (at least) iOS 7 and 8 and that there does not seem to be a workaround. I will update here if I receive notice of a fix.
There is actually workaround..
UIDevice.current.proximityState is changing as it should change. but the problem is with the UIDeviceProximityStateDidChange notification.
So the workaround is to create timer that check every x time if UIDevice.current.proximityState changed and that it. just don't use UIDeviceProximityStateDidChange.