Related
I am trying to clear the pasteboard after a string is copied after 10s. The requirements are the following:
After 10s, the copied text is cleared and therefore not pasteable in
the current app and other apps as well(ex. iMessage, Safari)
If non-identical text is copied, when the 10s is up the timer will not wipe it out
Attempts
I have tried doing this with only DispatchQueue.main.async however, this was freezing the original app.
I have tried doing it with only DispatchQueue.global(qos: .background).async however, when I switched to another app(iMessage), after 10s I could still paste the number. I had to go back to the original app and back to iMessage for it to be wiped out
This is my latest attempt and its the same behavior as #2, only getting wiped out when I go back to the original app and back to iMessage
private func clearTextAfterDelay(_ copiedCardNumber: String) {
expirationTimer?.invalidate()
expirationTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: false) { timer in
DispatchQueue.main.async {
let currentTextOnClipBoard = UIPasteboard.general.string
if currentTextOnClipBoard == copiedCardNumber {
UIPasteboard.general.setValue("", forPasteboardType: UIPasteboard.Name.general.rawValue)
}
}
}
DispatchQueue.global(qos: .background).async {
let runLoop = RunLoop.current
runLoop.add(self.expirationTimer!, forMode: .default)
runLoop.run()
}
}
Along with this article and the above comment I was able to figure it out https://medium.com/#abhimuralidharan/finite-length-tasks-in-background-ios-swift-60f2db4fa01b. Cheers
class ViewController: MvpViewController {
private var expirationTimerforBackground: Timer?
private var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier.invalid
private func clearTextAfterDelay(_ copiedCardNumber: String) {
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
self?.endBackgroundTask()
}
assert(backgroundTask != UIBackgroundTaskIdentifier.invalid)
self.expirationTimerforBackground?.invalidate()
self.expirationTimerforBackground = Timer.scheduledTimer(withTimeInterval: 10, repeats: false) { [weak self] _ in
let currentTextOnClipBoard = UIPasteboard.general.string
if currentTextOnClipBoard == copiedCardNumber {
UIPasteboard.general.setValue("", forPasteboardType: UIPasteboard.Name.general.rawValue)
}
self?.endBackgroundTask()
}
}
private func endBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskIdentifier.invalid
}
}
Number 2 doesn't work because your app gets suspended almost immediately upon resigning active. So you'd need to extend your app's active time by using background tasks.
Take a look at the beginBackgroundTaskWithExpirationHandler docs.
This method requests additional background execution time for your app. Call this method when leaving a task unfinished might be detrimental to your app’s user experience. For example, call this method before writing data to a file to prevent the system from suspending your app while the operation is in progress.
The app Snapchat, on the App Store, is an app that lets you share pictures with a self-destruct on them. You can only view the pics for X seconds. If you attempt to take a screenshot while the picture is showing using the home-power key combo, it will tell the sender you tried to take a screenshot.
What part of the SDK lets you detect that the user is taking a screenshot? I did not know this was possible.
As of iOS 7 the other answers are no longer true. Apple has made it so touchesCancelled:withEvent: is no longer called when the user takes a screenshot.
This would effectively break Snapchat entirely, so a couple betas in a new solution was added. Now, the solution is as simple as using NSNotificationCenter to add an observer to UIApplicationUserDidTakeScreenshotNotification.
Here's an example:
Objective C
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
object:nil
queue:mainQueue
usingBlock:^(NSNotification *note) {
// executes after screenshot
}];
Swift
NotificationCenter.default.addObserver(
forName: UIApplication.userDidTakeScreenshotNotification,
object: nil,
queue: .main) { notification in
//executes after screenshot
}
I found the answer!! Taking a screenshot interrupts any touches that are on the screen. This is why snapchat requires holding to see the picture. Reference: http://tumblr.jeremyjohnstone.com/post/38503925370/how-to-detect-screenshots-on-ios-like-snapchat
Heres how to do in Swift with closures:
func detectScreenShot(action: () -> ()) {
let mainQueue = NSOperationQueue.mainQueue()
NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationUserDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
detectScreenShot { () -> () in
print("User took a screen shot")
}
Swift 4.2
func detectScreenShot(action: #escaping () -> ()) {
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
This is included as a standard function in:
https://github.com/goktugyil/EZSwiftExtensions
Disclaimer: Its my repo
Swift 4+
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: OperationQueue.main) { notification in
//you can do anything you want here.
}
by using this observer you can find out when user takes a screenshot, but you can not prevent him.
Latest SWIFT 3:
func detectScreenShot(action: #escaping () -> ()) {
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
In viewDidLoad, call this function
detectScreenShot { () -> () in
print("User took a screen shot")
}
However,
NotificationCenter.default.addObserver(self, selector: #selector(test), name: .UIApplicationUserDidTakeScreenshot, object: nil)
func test() {
//do stuff here
}
works totally fine. I don't see any points of mainQueue...
Looks like there are no direct way to do this to detect if user has tapped on home + power button. As per this, it was possible earlier by using darwin notification, but it doesn't work any more. Since snapchat is already doing it, my guess is that they are checking the iPhone photo album to detect if there is a new picture got added in between this 10 seconds, and in someway they are comparing with the current image displayed. May be some image processing is done for this comparison. Just a thought, probably you can try to expand this to make it work. Check this for more details.
Edit:
Looks like they might be detecting the UITouch cancel event(Screen capture cancels touches) and showing this error message to the user as per this blog: How to detect screenshots on iOS (like SnapChat)
In that case you can use – touchesCancelled:withEvent: method to sense the UITouch cancellation to detect this. You can remove the image in this delegate method and show an appropriate alert to the user.
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
NSLog(#"Touches cancelled");
[self.imageView removeFromSuperView]; //and show an alert to the user
}
Swift 4 Examples
Example #1 using closure
NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot,
object: nil,
queue: OperationQueue.main) { notification in
print("\(notification) that a screenshot was taken!")
}
Example #2 with selector
NotificationCenter.default.addObserver(self,
selector: #selector(screenshotTaken),
name: .UIApplicationUserDidTakeScreenshot,
object: nil)
#objc func screenshotTaken() {
print("Screenshot taken!")
}
The app Snapchat, on the App Store, is an app that lets you share pictures with a self-destruct on them. You can only view the pics for X seconds. If you attempt to take a screenshot while the picture is showing using the home-power key combo, it will tell the sender you tried to take a screenshot.
What part of the SDK lets you detect that the user is taking a screenshot? I did not know this was possible.
As of iOS 7 the other answers are no longer true. Apple has made it so touchesCancelled:withEvent: is no longer called when the user takes a screenshot.
This would effectively break Snapchat entirely, so a couple betas in a new solution was added. Now, the solution is as simple as using NSNotificationCenter to add an observer to UIApplicationUserDidTakeScreenshotNotification.
Here's an example:
Objective C
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
object:nil
queue:mainQueue
usingBlock:^(NSNotification *note) {
// executes after screenshot
}];
Swift
NotificationCenter.default.addObserver(
forName: UIApplication.userDidTakeScreenshotNotification,
object: nil,
queue: .main) { notification in
//executes after screenshot
}
I found the answer!! Taking a screenshot interrupts any touches that are on the screen. This is why snapchat requires holding to see the picture. Reference: http://tumblr.jeremyjohnstone.com/post/38503925370/how-to-detect-screenshots-on-ios-like-snapchat
Heres how to do in Swift with closures:
func detectScreenShot(action: () -> ()) {
let mainQueue = NSOperationQueue.mainQueue()
NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationUserDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
detectScreenShot { () -> () in
print("User took a screen shot")
}
Swift 4.2
func detectScreenShot(action: #escaping () -> ()) {
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
This is included as a standard function in:
https://github.com/goktugyil/EZSwiftExtensions
Disclaimer: Its my repo
Swift 4+
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: OperationQueue.main) { notification in
//you can do anything you want here.
}
by using this observer you can find out when user takes a screenshot, but you can not prevent him.
Latest SWIFT 3:
func detectScreenShot(action: #escaping () -> ()) {
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
In viewDidLoad, call this function
detectScreenShot { () -> () in
print("User took a screen shot")
}
However,
NotificationCenter.default.addObserver(self, selector: #selector(test), name: .UIApplicationUserDidTakeScreenshot, object: nil)
func test() {
//do stuff here
}
works totally fine. I don't see any points of mainQueue...
Looks like there are no direct way to do this to detect if user has tapped on home + power button. As per this, it was possible earlier by using darwin notification, but it doesn't work any more. Since snapchat is already doing it, my guess is that they are checking the iPhone photo album to detect if there is a new picture got added in between this 10 seconds, and in someway they are comparing with the current image displayed. May be some image processing is done for this comparison. Just a thought, probably you can try to expand this to make it work. Check this for more details.
Edit:
Looks like they might be detecting the UITouch cancel event(Screen capture cancels touches) and showing this error message to the user as per this blog: How to detect screenshots on iOS (like SnapChat)
In that case you can use – touchesCancelled:withEvent: method to sense the UITouch cancellation to detect this. You can remove the image in this delegate method and show an appropriate alert to the user.
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
NSLog(#"Touches cancelled");
[self.imageView removeFromSuperView]; //and show an alert to the user
}
Swift 4 Examples
Example #1 using closure
NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot,
object: nil,
queue: OperationQueue.main) { notification in
print("\(notification) that a screenshot was taken!")
}
Example #2 with selector
NotificationCenter.default.addObserver(self,
selector: #selector(screenshotTaken),
name: .UIApplicationUserDidTakeScreenshot,
object: nil)
#objc func screenshotTaken() {
print("Screenshot taken!")
}
I have a UIView that is updated from a server once a button is pushed.
The UIView update happens within a dispatch_async(). Through debugging I can see that the server response has been received and the new subview is created and applied to the UIView.
However the view remains unchanged UNTIL another iOS notification occurs (e.g. the device receives an email). As soon as the notification banner is displayed the UIView is refreshed and displays the image from the server.
What am I missing? How do I get the UIView to update as soon as the new subview is added?
Note: I have tried the following within and outside the dispatch_async() and tried calling them on the same queue after the initial change takes place without luck.
self.view.setNeedsDisplay()
self.view.bringSubviewToFront(view)
self.view.layoutIfNeeded() (thanks for the suggestion #buxik)
EDIT added code (small version of larger class, only relevant section added):
class networkedImage: UIViewController {
#IBOutlet var viewArea: UIView = UIView()
let originalWidth: CGFloat = 200
let originalHeight: CGFloat = 100
let imageURL: NSURL = NSURL(string: "www.example.com/image.jpg")!
func updateViewArea() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
dispatch_async(dispatch_get_main_queue()) {
self.viewArea.subviews.forEach({ $0.removeFromSuperview() })
let newImageView = UIImageView(frame: CGRectMake(0, 0, self.originalWidth, self.originalHeight))
let data = NSData(contentsOfURL: self.imageURL)
if data != nil {
let image = UIImage(data: data!)
if image != nil {
newImageView.image = image
self.viewArea.addSubview(newImageView)
self.viewArea.bringSubviewToFront(newImageView)
self.viewArea.setNeedsDisplay()
}
NSNotificationCenter.defaultCenter().addObserverForName("NotificationIdentifier", object: nil, queue: nil, usingBlock: {
[unowned self] note in
print("I thought this might work, it didn't.")
})
}
}
}
}
}
To resolve the issue I added an Observer to viewDidLoad()
NSNotificationCenter.defaultCenter().addObserverForName("imageReadyNotification", object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: {
[unowned self] note in
self.updateViewArea()
})
Then at the bottom of updateViewArea() I post the notification
if self.newImage == true {
NSNotificationCenter.defaultCenter().postNotificationName("imageReadyNotification", object: nil, userInfo: nil)
}
The if condition is to ensure the notification isn't fired continually. I have no idea why calling this function twice works, but it does.
The app Snapchat, on the App Store, is an app that lets you share pictures with a self-destruct on them. You can only view the pics for X seconds. If you attempt to take a screenshot while the picture is showing using the home-power key combo, it will tell the sender you tried to take a screenshot.
What part of the SDK lets you detect that the user is taking a screenshot? I did not know this was possible.
As of iOS 7 the other answers are no longer true. Apple has made it so touchesCancelled:withEvent: is no longer called when the user takes a screenshot.
This would effectively break Snapchat entirely, so a couple betas in a new solution was added. Now, the solution is as simple as using NSNotificationCenter to add an observer to UIApplicationUserDidTakeScreenshotNotification.
Here's an example:
Objective C
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationUserDidTakeScreenshotNotification
object:nil
queue:mainQueue
usingBlock:^(NSNotification *note) {
// executes after screenshot
}];
Swift
NotificationCenter.default.addObserver(
forName: UIApplication.userDidTakeScreenshotNotification,
object: nil,
queue: .main) { notification in
//executes after screenshot
}
I found the answer!! Taking a screenshot interrupts any touches that are on the screen. This is why snapchat requires holding to see the picture. Reference: http://tumblr.jeremyjohnstone.com/post/38503925370/how-to-detect-screenshots-on-ios-like-snapchat
Heres how to do in Swift with closures:
func detectScreenShot(action: () -> ()) {
let mainQueue = NSOperationQueue.mainQueue()
NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationUserDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
detectScreenShot { () -> () in
print("User took a screen shot")
}
Swift 4.2
func detectScreenShot(action: #escaping () -> ()) {
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
This is included as a standard function in:
https://github.com/goktugyil/EZSwiftExtensions
Disclaimer: Its my repo
Swift 4+
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: OperationQueue.main) { notification in
//you can do anything you want here.
}
by using this observer you can find out when user takes a screenshot, but you can not prevent him.
Latest SWIFT 3:
func detectScreenShot(action: #escaping () -> ()) {
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot, object: nil, queue: mainQueue) { notification in
// executes after screenshot
action()
}
}
In viewDidLoad, call this function
detectScreenShot { () -> () in
print("User took a screen shot")
}
However,
NotificationCenter.default.addObserver(self, selector: #selector(test), name: .UIApplicationUserDidTakeScreenshot, object: nil)
func test() {
//do stuff here
}
works totally fine. I don't see any points of mainQueue...
Looks like there are no direct way to do this to detect if user has tapped on home + power button. As per this, it was possible earlier by using darwin notification, but it doesn't work any more. Since snapchat is already doing it, my guess is that they are checking the iPhone photo album to detect if there is a new picture got added in between this 10 seconds, and in someway they are comparing with the current image displayed. May be some image processing is done for this comparison. Just a thought, probably you can try to expand this to make it work. Check this for more details.
Edit:
Looks like they might be detecting the UITouch cancel event(Screen capture cancels touches) and showing this error message to the user as per this blog: How to detect screenshots on iOS (like SnapChat)
In that case you can use – touchesCancelled:withEvent: method to sense the UITouch cancellation to detect this. You can remove the image in this delegate method and show an appropriate alert to the user.
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
NSLog(#"Touches cancelled");
[self.imageView removeFromSuperView]; //and show an alert to the user
}
Swift 4 Examples
Example #1 using closure
NotificationCenter.default.addObserver(forName: .UIApplicationUserDidTakeScreenshot,
object: nil,
queue: OperationQueue.main) { notification in
print("\(notification) that a screenshot was taken!")
}
Example #2 with selector
NotificationCenter.default.addObserver(self,
selector: #selector(screenshotTaken),
name: .UIApplicationUserDidTakeScreenshot,
object: nil)
#objc func screenshotTaken() {
print("Screenshot taken!")
}