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

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

Related

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

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

Xcode program: Text box that the user can interact with through touch

I am new to Xcode and Swift.
I want to create a text box for a video game that acts as the dialogue between the game and the user. for example I want the text box to say (character name) walked 10 paces or "Used 'detect evil' orc within 60'. "
I also want the ability for the user to pressure touch this chat text message which is sent from the computer and then do a series of actions. For example if a paladin detects evil maybe the next course of action would be to hide or prepare to fight. Any ideas on how to set this up? I have been trying to create this on Main.storyboard. Can I only achieve this through the ViewController or one of the delegates?
First of all, you can only achieve this by coding (by using Storyboards as your ally of course!)
If you want to have a text box that is "clickable", what first comes to mind is just a button, that have no background color, so it looks just like text.
Then you need to create a method inside of your ViewController, ideally something like:
#IBAction func dialogBoxTapped(_ sender: Any?) {
// your code goes here
}
And connect your button to this function by setting up a touchUpInside method in the Storyboard.

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.

The handler of a UIAlertAction is a bit too late - how can I make it immediate?

I am trying (...) to add a sound effect to the buttons added to a UIAlertController. I fire a sound effect in the handler, but this actually is a bit too late. The sound fires like 0.5 seconds too late. I want the sound to fire as soon as the alert is about to dismiss, not after it has dismissed. With UIAlertView this was possible to handle using alertWillDismiss... rather than alertDidDismiss.
Did I miss something?
No, you didn't miss anything. The functionality you're looking for is not provided by UIAlertController. Consider providing your own presented view controller, over which you'll have the kind of fine control you're after.
I used Patrick Goley's suggestion, namely to subclass UIAlertController and override viewWillDisappear. Worked great for me.
//
// ImmediateClickAlertController.swift
//
// This subclass of UIAlertController plays a click immediately whenever it is dismissed (i.e. when a button is tapped).
// This fixes an issue when trying to play a click in an attached UIAlertAction, which does not happen until after its view disappears.
import AudioToolbox
class ImmediateClickAlertController: UIAlertController {
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
// play a click
let pressKeySystemSoundID: SystemSoundID = 1104
AudioServicesPlaySystemSound(pressKeySystemSoundID)
}
}
A bit of a hackery, but perhaps you could:
Try and grab a reference to the alert's button (by traversing the -admittedly private- view hierarchy tree), and
Use KVO to detect any changes to its selected and/or highlighted properties (my own experience is that selected is not reliably observable, while highlighted is).
...but all of this is quite fragile, not elegant, maight break in a future release of the OS and/or get you rejected from the app store...?
So you're best option (even if the most laborious) is to roll your own modal view controller:
Apple's documentation on presenting modal view controllers.
Demo project I made following the above docs (a custom "UIAlertController look-alike" with an embedded UIActivityIndicator - for use during long, asynchronous processes):

Replace views in detail part of the screen after user's action

I am new to Swift and app development. I have a design question. I am trying to make a view that contains a slider, but that as soon as the "touch up inside" action is performed, is replaced by a progress bar + button. If the button is pressed, then we go back to showing only the slider. This view will be not take the whole screen, only part of it.
What would be the best way of doing this? I have already investigated several options:
1. using a navigation controller with a segue triggered by the slider that goes into a new scene with a progress bar & button.
2. creating a custom view with two properties: a slider and a custom view (progress bar & button). The slider can be laid out using interface builder, and the custom view can be loaded from a nib file when needed.
3. creating a custom view with two properties: a slider and a custom view (progress bar & button). The new progress bar and button are created programmatically whenever the action is triggered on the slider.
I have already tried options 1 and 2 to some extent with no success. Since I am a beginner, I am trying to use the IB as much as possible. What is the best option (if any) from the list?
You can do this directly on the Storyboard without needing to create a custom view class, but you'll need a few lines of code in any case. Just drag a Slider into your View, and then drag a button and a progress view directly on top of that. Now select the button, and in the Attributes inspector, tick the box next to "Hidden". Do the same with the progress bar. Then just open the assistant editor and connect references to all 3 of those. You'll also need to create an action for the button (I've called it change), and make sure you leave the type field as AnyObject. Add the following line inside ViewDidLoad:
slider.addTarget(self, action: Selector("change:"), forControlEvents: .TouchUpInside)
This line just makes it so that change gets called anytime the user uses the slider. Obviously change slider to whatever you name your UISlider. You can implement the change function like this:
#IBAction func change(sender: AnyObject) {
slider.hidden = !slider.hidden
button.hidden = !button.hidden
progressBar.hidden = !progressBar.hidden
}
This is a simple implementation that just toggles between true and false for each of the items, but you'll probably want to do it differently depending on what this project does.
Now, if you want to put this functionality in multiple places in your app it might be easiest to create a custom view using the same concept as above, in which case check out this tutorial on how to create an IBDesignable UIView.

Resources