How to mask UIView right before a user takes a screenshoot? - ios

Since iOS doesn't let us disable screenshoots (from what I know), I want to mask/overwrite a UIView right before the user takes a screenshoot and then remove the mask view when the screenshoot is done. I've been researching for quite a bit but still don't get any working answers. My code looks like this so far:
override func viewDidLoad() {
super.viewDidLoad()
...
NotificationCenter.default.addObserver(forName: UIApplication.userDidTakeScreenshotNotification, object: nil, queue: .main, using: { [weak self] _ in
self?.addMaskView() // This adds a custom view to the top to hide all of the content in the ViewController
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
self?.removeMaskView() // Removes the custom view
})
})
}
But what this does is it added the mask view for 1 second, and hides it. But when I look at the screenshoot, it still shows the full content and not the mask view. Is there any other way to do what I want to achieve?

The observer you are are working with posts the notification after the user takes screenshot thats why its not working as you intended. The most effective way is to delete the screenshot as soon as it is created via the application.
As soon as the app goes to background, delete the last screenshot created
trigger a function as soon as you receive the post that the user took a screenshot, the function will override the same image with some default image from your side
Also make sure the application can perform the same function while staying in background so the only way user can get scr if they instantly kill the application after getting a scr
2. You can also trigger a transparent screenoverlay that play drm protected content upon detecting screenshot. then your screenshots will be masked.
both ways are not that great but still they are only options right now

Related

UIRefreshControl is flickering and transitions are abrupt

I'd like to set up a UIRefreshControl in my apps' main table view controller to trigger a refresh function to (you probably guessed it by now) refresh it.
Adding it isn't the problem, I'm able to set it up through the storyboard (by enabling refreshing) or in the controller (by using self.refreshControl = UIRefreshControl()).
When it gets triggered by the user it starts refreshing normally, but when I trigger it through self.refreshControl.beginRefreshing() the control isn't displayed in the navigation bar, until I scroll down (like you would normally trigger it)
But that's not even the weirdest part. When it's finally visible to the user there is a constant flicker to it and after a while it's not visible at all.
For the self.refreshControl.endRefreshing() method, the animation is really abrupt any choppy and sometimes the navigation bar doesn't scroll up again (leaving an empty space at the top)
Here is a gif, that should summarise my problem
I use swift 4.2, the application was tested on different simulators (running iOS 12.1) and my iPhone X (running iOS 12.1.2) and the release target is iOS 10.0.
I've already done research on my problem, but no one seems to have the exact issue (at least the flickering) or the solutions don't work for me.
This is how my view controller is set up:
class MyViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.refreshControl = UIRefreshControl()
self.refreshControl?.addTarget(self, action: #selector(reload), for: .valueChanged)
self.refreshControl.beginRefreshing()
}
#objc func reload() {
DispatchQueue(label: "update").async {
sleep(10)
DispatchQueue.main.async {
self.refreshControl?.endRefreshing()
}
}
}
}
Thanks for reading, I really hope someone can help me.
Update
I just discovered that the issue only occurs with a non-translucent navigation bar with large title, so picking a translucent navigation bar instead of the opaque is a solution (sadly not one I would like to live with). Maybe someone has an approach to having a functional refresh control with an opaque navigation bar.
Additionally I submitted a bug report to Apple, because something like that should not happen in the first place, I'll update this post when they get back to me.

Why doesn't my button text show when on top of AVCaptureVideoPreviewLayer?

I have a QR Code Scanner view in which I have an AVCaptureVideoPreviewLayer and a UIButton.
When this view displays, the text within the button does not show. It should display the word 'Cancel'. If I touch the button, or swipe the button - but not tap it, the button text will show at that point.
Does anyone know how I can get the button text to display properly?
Here's what my view hierarchy looks like:
When I first enter the Scanner view, the buttons look like this:
There is no text. The text will show only after I touch the buttons:
One last thing, it seems this is an issue only is iOS 10...
Any suggestions welcome. Thanks!
Yes. You need to add an overlay view.
Main view containing video preview and overlay view.
Overlay view contains buttons.
For each button, you can use these events if you want to animate:
TouchDown
TouchUpInside
TouchOutInside
As the documentation of AVCaptureSession states, startRunning() call blocks the main thread so your UI is not drawn correctly.
The startRunning() method is a blocking call which can take some time,
therefore you should perform session setup on a serial queue so that
the main queue isn't blocked (which keeps the UI responsive). See
AVCam-iOS: Using AVFoundation to Capture Images and Movies for an
implementation example.
If you check Apple's example (Swift 3 and Objective-C), you can easily set a new queue for those actions without blocking your main thread.
This is the example for Swift 2.3 if you need it.
// Swift 2.3
private let sessionQueue = dispatch_queue_create("session queue", nil)
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(sessionQueue) {
self.configureSession()
}
dispatch_async(dispatch_get_main_queue()) {
self.setupScreen()
}
}
Use layer.insertSublayer(..., above: ...) or layer.insertSublayer(..., below: ...) to control your CALayers
Here's the similar answer link or
a bit more detailed instructions for Swift how to insert image on the top of video stream manual

Long delay in displaying screen swift 2.2

My application reads data from MySql server and displays it in a tableview. At the start of the call to the server the application covers the tableView with a view (named "pdView") that has background colour of Light Gray. That view is part of the scene which the tableView is also part of (designed in IB), but is in a hidden state, until just before the call to the server where it becomes visible using
pdView.hidden = false
After the app gets the data and fills the tableview, using
dispatch_async(dispatch_get_main_queue(), { () -> Void in
tableview.reloadData()
})
one can see the data displayed in the tableView, under the gray display.
I then try to hide back the view using
pdView.hidden = true
but it takes the app about 44 seconds for the gray color to disappear. During that time my app behaves normally and I can scroll the table up and down.
I tried putting the code to hide the view inside dispatch_async() but to no avail.
What needs to be done to fix this problem so that the pdView disappear right after it is back to the hidden state?
ALL your UI code needs to be from the main thread. That includes things like changing the state of a view's hidden flag.
Do you have that code wrapped in a call to
dispatch_async(dispatch_get_main_queue()
as well?
It sounds like you are trying to update the UI from a separate thread. You will need to give your pdView.hidden = true some context. You can try
dispatch_async(dispatch_get_main_queue(), { () -> Void in
tableview.reloadData()
self.pdView.hidden = true
})
Note the self in self.pdView.hidden..
Hope that helps.

Xcode Display a loading view until the second view loads (on a segue show)

I have an app that switch views using a segue when a button is clicked.
The second view loads data from the internet and it can take a couple of seconds.
I would like to know how can i display a loading view/splash screen in the meantime so the view could finish the loading and the app wont appear like it's doing nothing.
Thanks!
Check this library SwiftSpinner. It serves the purpose of your needs. It's really brilliant.
Call the necessary function from the library in the viewDidLoad method of your ViewController which loads the data from the internet. Remove this view in DidFinishLoading method of the NSURLProtocol (It's an optional func declared in that class which detects when the request to that URL is complete). The documentation is given in that library itself.
Sounds like you're looking for an activity indicator. I've used the custom class posted https://stackoverflow.com/a/32661590/3516923 with success. Just a note of warning, in his class he blocks all input while the indicator is in view. If you want to make it so your users can back out before things finish you need to remove UIApplication.sharedApplication().beginIgnoringInteractionEvents() and UIApplication.sharedApplication().endIgnoringInteractionEvents() from the start and stop animating functions.
If what you want is really a splash screen, have a UIImageView underneath the view that you're loading. Set the image to your splash screen image. Set the loading view to hidden=YES before it's shown, then set hidden to NO after it finishes loading. You could even set the opacity of the frontmost view to give you a fading effect.
1.You need to find a kind of indicator, suck like an activity indicator or something else to show the loading UI to the user.
2.Set the user interaction unable, so that the user won`t touchup inside repeatedly.
3.Start the indicator, set the user interaction unable when you load the server data, and stop the indicator animation when you finish, hide the indicator, enabled the user interaction.

Delay when using instantiateViewControllerWithIdentifier but not performSegueWithIdentifier?

The code below is used to push another view controller onto the navigation stack.
When using instantiateViewControllerWithIdentifier, the segue is noticeably sluggish the first time (~3 seconds) but occurs reasonably fast each subsequent time. Other SO posts suggested ensuring the segue occurs on the main thread, which the code accomplishes, but this didn't fix the problem.
However, using performSegueWithIdentifier causes no delay.
The viewDidLoad code for SendViewController is the same for the first and subsequent pushes.
Tried blanking out viewDidLoad for the destination view controller, but still the lag exists for instantiateViewControllerWithIdentifier but not for performSegueWithIdentifier.
How to fix the delay with instantiateViewControllerWithIdentifier?
No delay:
#IBAction func buttonTapped(sender: UIButton) {
performSegueWithIdentifier(SendSegue, sender: self)
}
Results in delay when showing SendViewController for first time:
#IBAction func buttonTapped(sender: UIButton) {
dispatch_async(dispatch_get_main_queue()) {
let vc = self.storyboard!.instantiateViewControllerWithIdentifier(self.SendViewControllerID) as! SendViewController
self.navigationController!.pushViewController(vc, animated: true)
}
}
This issue could occur in many different scenarios. The best way determine what is causing your specific problem is by profiling with the instruments included in Xcode.
Click and hold the Build button in your xcode window. You will see four options appear, select Profile.
Once the build runs a window with instruments will pop up. Select, Time Profiling from the options.
A new window will appear with various metrics in it. The top left corner will have a red record button. Click the red record button and this will launch the app on your phone.
Proceed to the transition giving you problems. End the recording after the transition occurs by selecting the same button you started the recording with.
Review the "Details" pane in the bottom left corner. You will see a column titled "Running time" that shows the time it took to execute every method in your code (both OS methods and user generated code)
Determine if anything is out of place or occurs that is not intended. Possibly go back and execute the transition again to compare the difference between the two. Clicking the function in the list will take you directly to the code being executed. This can be very helpful.
It is very likely that if a transition takes 3-5 seconds one particular function will be obvious when following these steps. Happy profiling!
WWDC from last year has a great segment on this as well. Def worth checking out here: (open in Safari only) WWDC Profiling Talk
The problem was isolated to the presence of a UITextField in the destination view controller, that is, removing the UITextField removes the lag.
Then it was further isolated to the presence of a custom font.
In other words, using the system font on the UITextField, rather than a custom font, removes the lag. No explanation why, but it works.
After time profiling I realized it was the call to instantiateViewController which I couldn't find anything that could help me with that.
Unfortunately, the only thing that worked was either using a separate storyboard for that view controller and instantiating it from there, or redoing the view controller programmatically.

Resources