I have set up my UITableView to use the new Drag and Drop APIs.
if #available(iOS 11, *) {
self.tableView.dragDelegate = self
self.tableView.dropDelegate = self
self.tableView.dragInteractionEnabled = true
navigationController?.navigationBar.prefersLargeTitles = false
}
Now, I implemented the method below to be able to use custom views for the d&d.
#available(iOS 11.0, *)
func dragInteraction(_ interaction: UIDragInteraction, previewForLifting item: UIDragItem, session: UIDragSession) -> UITargetedDragPreview? {
print("Custom Preview method called!")
let test = UITextView.init(frame: CGRect.init(x: 0, y: 0, width: 200, height: 200))
test.text = "sfgshshsfhshshfshsfh"
let dragView = interaction.view!
let dragPoint = session.location(in: dragView)
let target = UIDragPreviewTarget(container: dragView, center: dragPoint)
return UITargetedDragPreview(view: test, parameters:UIDragPreviewParameters(), target:target)
}
However, this method never gets called. I never see the print() or my custom view. Any ideas as to what I'm doing wrong?
You have to set previewProvider property when creating the UIDragItem.
let dragItem = UIDragItem(...)
dragItem.previewProvider = {
print("Custom Preview method called!")
let test = UITextView.init(frame: CGRect.init(x: 0, y: 0, width: 200, height: 200))
test.text = "sfgshshsfhshshfshsfh"
return UIDragPreview(view: test)
}
See: https://developer.apple.com/documentation/uikit/uidragitem/2890972-previewprovider?language=objc
Are you using an iPhone? I am not certain it is currently working for customer UIViewController as of November 25, 2017 based on the following:
I downloaded the Apple sample project for drag-and-drop on an iPad
I added a breakpoint in dragInteraction and confirmed I could reach it
I modified the target to be a universal app (i.e. iPhone, too)
I ran on an iPhone 8 device and did not reach the breakpoint
As further suggestion, there is a property named dragInteractionEnabled for both UITableViewController and UICollectionViewController. For iPhone devices, the property is defaulted to false and, more importantly, the property is not even defined higher in UIViewController.
Related
I want to create a Floating Button in my app which will be visible all over the page and can handle the event from a separate ViewController to maintain the button state.
I want to open a ViewController from a separate window at the same time when the user login into the app. And if the user clicks on the button then ViewController will handle the event and if click outside the button then it should handle by the main controller.
Thanks
First Create a UIViwe with a .xib file and create your required UI. All UI-related functions will be there in UIView class and for action create a manager class that manages all events.
Use the below code to add playerView to the window
PlayerManager.shared.addPlayer()
See bellow code for playerManager class
class PlayerManager : NSObject{
static let shared = PlayerManager()
private override init(){
super.init()
}
func addPlayer(){
let window = UIApplication.shared.keyWindow
let player = Bundle.main.loadNibNamed("PlayerView", owner: nil, options: nil)![0] as! PlayerView
let outerView = UIView(frame: CGRect(x: getXValueForView(width: 180), y: getYValueForView(height: 120), width: 180, height: 60))
player.frame.size = CGSize(width: 180, height: 60)
player.playerDelegate = self
outerView.clipsToBounds = true
player.miniPlayer.isHidden = false
player.expendedPlayer.isHidden = true
outerView.addSubview(player)
window?.addSubview(outerView)
}
}
I am working on implementing the Vonage video API on IOS devices. I would like to know if there is a way to have my publisher view inside of a container view. I currently have my video views hard coded so the position of these views look different on different devices. I am hoping to utilize auto layout but I am not sure how the publisher view will react with storyboard. Please let me know if there is a way I can combine the publisher view with storyboard and how to go about implementing this. Here is a snippet of the code I currently have:
guard let publisher = OTPublisher(delegate: self, settings: settings) else {
return
}
if ((session.capabilities?.canPublish) != nil) {
// The client can publish.
print("The client can publish.")
guard let publisherView = publisher.view else {
return
}
//added session.publish line
session.publish(publisher, error: &error)
let screenBounds = UIScreen.main.bounds
publisherView.frame = CGRect(x: screenBounds.width - 400, y: screenBounds.height - 850, width: 375, height: 350)
publisherView.layer.borderWidth = 5
publisherView.layer.borderColor = UIColor.white.cgColor
view.addSubview(publisherView)
Is it possible to put a loading animation over the VNDocumentViewController? As in, when the user presses the Save button, is there a way for me to somehow indicate that the Vision is processing the image and hasn't frozen? Right now, in my app, there is a long pause between the user pressing Save and the actual image being processed.Here is an example from another post of what I'm trying to create
Here is one example of adding a loading indicator using UIActivityIndicatorView().
startAnimating() to start the animation and stopAnimation() to stop the animation.
iOS - Display a progress indicator at the center of the screen rather than the view
guard let topWindow = UIApplication.shared.windows.last else {return}
let overlayView = UIView(frame: topWindow.bounds)
overlayView.backgroundColor = UIColor.clear
topWindow.addSubview(overlayView)
let hudView = UIActivityIndicatorView()
hudView.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)
overlayView.addSubview(hudView)
hudView.center = overlayView.center
hudView.startAnimating()
Alternatively, you could look into using Cocoapod MBProgressHud
https://cocoapods.org/pods/MBProgressHUD
There's a way you can extend a class in Swift that captures this problem well. The idea is you want a UIActivityIndicator in your VNDocumentCameraViewController. But we'd like that to be a part of every version of this we use. We could simply embed the DocumentVC's view into our current view and superimpose a UIActivityIndicator above it in the view stack, but that's pretty hacky. Here's a quick way we can extend any class and solve this problem
import VisionKit
import UIKit
extension VNDocumentCameraViewController {
private struct LoadingContainer {
static var loadingIndicator = UIActivityIndicatorView()
}
var loadingIndicator: UIActivityIndicatorView {
return LoadingContainer.loadingIndicator
}
func animateLoadingIndicator() {
if loadingIndicator.superview == nil {
view.addSubview(loadingIndicator)
//Setup your constraints through your favorite method
//This constrains it to the very center of the controller
loadingIndicator.frame = CGRect(
x: view.frame.width / 2.0,
y: view.frame.height / 2.0,
width: 20,
height: 20)
//Setup additional state like color/etc here
loadingIndicator.color = .white
}
loadingIndicator.startAnimating()
}
func stopAnimatingLoadingIndicator() {
loadingIndicator.stopAnimating()
}
}
The place we can call these functions are in the delegate methods for VNDocumentCameraViewController that you implement in your presenting ViewController:
func documentCameraViewController(
_ controller: VNDocumentCameraViewController,
didFinishWith scan: VNDocumentCameraScan
) {
controller.animateLoadingIndicator()
}
How to remove subviews?
I am trying to integrate GIF by creating UIView and UIImageView programmatically.
It works fine to show GIF but when the function of hiding if is called, there is no response.
Here are the codes of both functions.
class CustomLoader: UIView {
static let instance = CustomLoader()
var viewColor: UIColor = .black
var setAlpha: CGFloat = 0.5
var gifName: String = ""
lazy var transparentView: UIView = {
let transparentView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
transparentView.backgroundColor = viewColor.withAlphaComponent(setAlpha)
transparentView.isUserInteractionEnabled = false
return transparentView
}()
lazy var gifImage: UIImageView = {
var gifImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
gifImage.contentMode = .scaleAspectFit
gifImage.center = transparentView.center
gifImage.isUserInteractionEnabled = false
gifImage.loadGif(name: gifName)
return gifImage
}()
func showLoaderView() {
self.addSubview(self.transparentView)
self.transparentView.addSubview(self.gifImage)
self.transparentView.bringSubview(toFront: self.gifImage)
UIApplication.shared.keyWindow?.addSubview(transparentView)
}
func hideLoaderView() {
self.transparentView.removeFromSuperview()
}
}
A couple of thoughts:
I’d suggest you add a breakpoint or a logging statement in hideLoaderView and make sure you’re getting to that line.
You should make the init method to this class private to make sure you’re not calling hideLoaderView on some separate instance. When dealing with singletons, you want to make sure you can’t accidentally create another instance.
But I tested your code, and it works fine. Your problem probably rests with where and how you call this (and making init private, you might find where you might be using it inappropriately).
In the comments below, you said:
I simply call the function "CustomLoader().hideLoaderView()" Both are being called technically. What do you mean by "where I using it inappropriately?"
That is the root of the problem.
The CustomLoader() of CustomLoader().hideLoaderView() will create a new instance of CustomLoader with its own transparencyView, etc., which is precisely what the problem is. You’re not hiding the old view that was presented earlier, but trying to hide another one that you just created and was never displayed.
If you instead use that static, e.g. CustomLoader.instance.showLoaderView() and CustomLoader.instance.hideLoaderView(), then the problem will go away. Then you will be hiding the same view that your previously showed.
By the way, a few other unrelated observations:
If this is a singleton or shared instance, the convention would be to call that static property shared, not instance.
By the way, you aren’t using this CustomLoader as a UIView, so I’d not make it a UIView subclass. Don’t make it a subclass of anything.
You would obviously eliminate that self.addSubview(transparentView) line, too.
The bringSubview(toFront:) call is unnecessary.
You should avoid referencing UIScreen.main.bounds. You don’t know if your app might be in multitasking mode (maybe this isn’t an issue right now, but it’s the sort of unnecessary assumption that will cause problems at some later date). Just refer to the bounds of the UIWindow to which you’re adding this. You should also update this frame when you show this view, not when you create it (in case you changed orientation in the intervening time, or whatever).
By the way, using keyWindow is discouraged in iOS 13 and later, so you might eventually want to remove that, too.
When adding the gifImage (which I’d suggest renaming to gifImageView because it’s an image view, not an image), you should not reference the center of its superview. That’s the coordinate of the transparent view in its super view’s coordinate system, which could be completely different than the transparent view’s own coordinate system. In this case, it just happens to work, but it suggests a fundamental misunderstanding of view coordinate systems. Reference the bounds of the transparentView, not its center.
If you’re going to expose viewColor and setAlpha, you should pull the setting of the transparentView’s color out of the lazy initializer and into showLoaderView, at the very least. Right now, if you show the loader once, and then change the color, and try to show it again, you won’t see the new color.
The same issue applies with the gif image. So, I’d move that to the didSet observer.
Thus, pulling this all together:
class CustomLoader{
static let shared = CustomLoader()
private init() { }
var dimmingColor: UIColor = .black
var dimmingAlpha: CGFloat = 0.5
var gifName: String = "" { didSet { gifImage.loadGif(name: gifName) } }
lazy var transparentView: UIView = {
let transparentView = UIView()
transparentView.isUserInteractionEnabled = false
return transparentView
}()
lazy var gifImageView: UIImageView = {
var gifImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
gifImage.contentMode = .scaleAspectFit
gifImage.isUserInteractionEnabled = false
return gifImage
}()
func showLoaderView() {
guard let window = UIApplication.shared.keyWindow else { return }
transparentView.frame = window.bounds
transparentView.backgroundColor = dimmingColor.withAlphaComponent(dimmingAlpha)
gifImageView.center = CGPoint(x: transparentView.bounds.midX, y: transparentView.bounds.midY)
transparentView.addSubview(gifImageView)
window.addSubview(transparentView)
}
func hideLoaderView() {
transparentView.removeFromSuperview()
}
}
Why you are using transparentView while you are have a CustomLoader instance view
Try to use this
class CustomLoader: UIView {
static let instance = CustomLoader()
var viewColor: UIColor = .black
var setAlpha: CGFloat = 0.5
var gifName: String = ""
init() {
super.init(frame: UIScreen.main.bounds)
backgroundColor = viewColor.withAlphaComponent(setAlpha)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var gifImage: UIImageView = {
var gifImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 200, height: 60))
gifImage.backgroundColor = .red
gifImage.contentMode = .scaleAspectFit
gifImage.center = center
gifImage.isUserInteractionEnabled = false
gifImage.loadGif(name: gifName)
return gifImage
}()
func showLoaderView() {
addSubview(self.gifImage)
UIApplication.shared.keyWindow?.addSubview(self)
}
func hideLoaderView() {
removeFromSuperview()
}
}
I got the problem that I'm loading a local html file in a WKWebview.
My procedure is working for iOS 8, 9, 11 but not for 10.3. There is just white space in the middle of the text.
I'm creating my WKWebview like this:
let webConfiguration = WKWebViewConfiguration();
let frame = CGRect(x: 0, y: 0, width: cell.frame.width, height: self.webViewHeight);
infoWebView = WKWebView(frame: frame, configuration: webConfiguration);
infoWebView.navigationDelegate = self;
infoWebView.translatesAutoresizingMaskIntoConstraints = false
infoWebView.scrollView.isScrollEnabled = false;
infoWebView.clipsToBounds = false;
infoWebView.allowsBackForwardNavigationGestures = false
infoWebView.contentMode = .scaleToFill
infoWebView.sizeToFit()
The loading of my html file is done by the following code:
if let availableUrl = url {
let urlRequest = URLRequest(url: availableUrl)
infoWebView.load(urlRequest)
}
Do you have an idea why it's working for iOS 8,9,11 but not for iOS 10.3?
Can somebody reproduce this problem?
Kind regards and have a nice day!
Since I had my wkwebview in a table view I had to override scrollViewDidScroll from UITableViewDelegate.
this code from WKWebView not rendering correctly in iOS 10 did the trick for me:
// in the UITableViewDelegate
func scrollViewDidScroll(scrollView: UIScrollView) {
if let tableView = scrollView as? UITableView {
for cell in tableView.visibleCells {
guard let cell = cell as? MyCustomCellClass else { continue }
cell.webView?.setNeedsLayout()
}
}
}