I am using QLPreviewController to display a PDF. Now I am trying to add button items to the toolbar. When I print the toolbarItems I can see they are being added but my toolbar will not appear. Why is it not appearing?
func previewController(controller: QLPreviewController, previewItemAtIndex index: Int) -> QLPreviewItem {
let rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(drawCircle))
let rightButton2 = UIBarButtonItem(title: "Title2", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(drawCircle))
controller.setToolbarItems([rightButton, rightButton2], animated: false)
print(controller.toolbarItems)
let localUrl = String(format:"%#/%#", PDFFilePath, fileNameGroup)
let url = NSURL.fileURLWithPath(localUrl)
return url
}
viewPDF = QLPreviewController()
viewPDF.dataSource = self
self.presentViewController(viewPDF, animated: true, completion: nil)
From what I infered, I am not sure that you are adding the buttons to the right position. But the problem is for that you need to make a custom view controller and add QLPreviewController as a subview. Something like this:
class CustomQLViewController: UIViewController {
var customQL=QLPreviewController()
#IBOutlet weak var qlView: UIView! //defined in storyboard for QLPreviewController
override dynamic func viewDidLoad() {
super.viewDidLoad()
customQL.dataSource = self
customQL.view.frame = CGRectMake(0, 0, qlView.frame.size.width, qlView.frame.size.height)
qlView.addSubview(customQL.view)
}
What you see here is a view controller added in the storyboard with a custom UIView, used to represent the region that will be used up by QLPreviewController. Just add the buttons you want in the storyboard and connect their outlets in the class.
The main thing here is to set the QLPreview controller to the dimensions of the view you added in the storyboard (ie. qlView) and add set the frame of the QLPreview object to qlView. The final step would be to add the QLpreview as a subview in qlView.
Related
I have two storyboard, SB_A,SB_B
So when I click the button at SB_A, it call
let storyboard: UIStoryboard = UIStoryboard(name: "SB_B", bundle: nil)
let nextView = storyboard.instantiateInitialViewController()
self.present(nextView!, animated: true, completion: nil)
So I can see SB_B storyboard.
But I can't see the back button at first scene in SB_B
I want put back button at right scene and when I click that, back to SB_A storyboard.
How I can put back button at first scene in storyboard?
add these lines in SB_B viewdidload
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(self.leftNavigationItemAction(_:)))
}
#objc func leftNavigationItemAction(_ sender: UIBarButtonItem) -> Void {
_ = popOrDismissViewController(true)
}
try this one...
No you can't use popviewonctroller or Dissmisviewcontroller to go back to previous storyboard's view controller you have to explicitly manage this back functionality to get required functionality using push or present.
I am making a custom camera in Swift. I declared it global like this:
let image = UIImagePickerController()
I have made OverlayVC (UIViewController) in IB. Made a shutter button and hooked it up like this:
#IBAction func shutterTapped(_ sender: Any) {
print("shutterTapped")
image.takePicture()
}
I instantiate this overlay before presenting:
image.delegate = self
image.sourceType = .camera
image.cameraDevice = .front
image.allowsEditing = false
let overlay = self.storyboard?.instantiateViewController(withIdentifier: "OverlayVC")
image.cameraOverlayView = overlay?.view
image.showsCameraControls = false
self.present(image, animated: true, completion: nil)
Now when I run the build on device and tap on the shutter button, I can visually see it being tapped (fade out/in) but the code in shutterTapped() never executes.
Why is it not really working?
Adding view of the view controller removes controller itself and no one can actually get the event. So, you get the view but not events. and to support this:
Solution that will actually work with your code would be:
image.cameraOverlayView = overlay?.view
if let viewController = overlay {
for view in viewController.view.subviews {
if let button = view as? UIButton {
button.addTarget(self, action: #selector(self.takePicture), for: UIControlEvents.touchDown)
}
}
}
You can also add tag to it so you can check which button is which if you have more of them.
Alternate Solution:
Create UIView subclass and xib for it. Same view you had in the OverlayVC. Then, when adding overlay use following:
image.sourceType = .camera
image.cameraDevice = .front
image.allowsEditing = false
let myOverlay = Bundle.main.loadNibNamed("CameraOverlay", owner: self, options: nil)
image.cameraOverlayView = myOverlay?.first as! UIView
image.showsCameraControls = false
self.present(image, animated: true, completion: nil)
Note that force unwrap should be protected with either guard or if let when casting to UIView.
Since I was having issues creating a button via storyboard, I went about initiating a right nav bar button through code - found via this question How can I go back to the initial view controller in Swift?. This code is meant to take me back to my root view controller.
So here is the code as it stands at the moment.
let button1 = UIBarButtonItem(image: UIImage(named: "HomeM25.png"), style: .plain, target: self, action: #selector(getter: UIDynamicBehavior.action))
self.navigationItem.rightBarButtonItem = button1
func button() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
I was under the impression that if I were to change the
action: #selector(getter: action)
I would be able to create a function following this button initialization like so
func action() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
However, I am greeted with the "Use of local variable 'action' before its declaration'". I do not understand why this interpretation would not perform/why I would have to establish the action variable when its only use is a function name? Any help would be appreciated.
Update 1: Current Code
override func viewDidLoad() {
super.viewDidLoad()
let button1 = UIBarButtonItem(image: UIImage(named: "HomeM25.png"), style: .plain, target: self, action: #selector(action))
self.navigationItem.rightBarButtonItem = button1
func action() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
Update 1: Also had to use a different function action to go back to the original view controller.
self.navigationController!.popToRootViewController(animated: true)
It should be #selector(action). And you can define function within a function. Move action outside of the viewDidLoad function. Try this.
override func viewDidLoad() {
super.viewDidLoad()
let button1 = UIBarButtonItem(image: UIImage(named: "HomeM25.png"), style: .plain, target: self, action: #selector(action))
self.navigationItem.rightBarButtonItem = button1
}
func action() {
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
}
I am looking for a way to call a custom alert view from multiple view controllers. So far I have made several different attempts without success.
I created an alert view with an interface builder that works fine on one view controller but not the other.
I then tried creating the alert view programmatically thinking it may have something to do with the outlets not being connected on the other view controller. This one also worked on one view controller and not the other.
I made a separate swift file and made a public function and the same result. With this last method, I am able to successfully re-use a regular UIAlertController on multiple view controllers but that is not exactly what I am looking for.
With the first two methods, I do not get any compiling errors. The app runs fine and then crashes when I call the alert from another view controller.
Thanks in advance for any input!
EDIT:
This example works when I put it in another swift file.
public func showSimpleAlert(title: String, message: String?, presentingController: UIViewController) {
if IS_OS_8_OR_LATER() {
let controller = UIAlertController(title: title, message: message, preferredStyle: .Alert)
controller.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: { (action) -> Void in
}))
presentingController.presentViewController(controller, animated: true, completion: nil)
} else {
let alert = UIAlertView(title: title, message: message, delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
}
This is the one I want to work on.
public func showAlert(oMsg: String, oTitle:String) {
alertView.backgroundColor = UIColor.whiteColor()
alertView.layer.cornerRadius = 25
alertTitleLabel.text = oTitle as String
alertTitleLabel.font = UIFont(name: "Open-Sans-Bold", size: 20)
alertTitleLabel.textColor = UIColor.blackColor()
alertTitleLabel.textAlignment = .Center
alertTitleLabel.numberOfLines = 1
alertTitleLabel.frame = CGRectMake(25, 60, 264, 112)
alertLabel.text = oMsg as String
alertLabel.font = UIFont(name: "Open-Sans", size: 20)
alertLabel.textColor = UIColor.blackColor()
alertLabel.textAlignment = .Center
alertLabel.numberOfLines = 4
alertLabel.frame = CGRectMake(25, 130, 264, 112)
okButton.setTitle("OK", forState: .Normal)
okButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
okButton.frame = CGRectMake(60, 230, 197, 75)
okButton.addTarget(UIViewController.self, action:#selector(LoginViewController.buttonAction(_:)), forControlEvents: .TouchUpInside)
}
I will give the answer for a simple custom alertview which is basically a modified uiviewcontroller. you can use a uiviewcontroller as a uialertviewcontroller as follow.
Simple AlertView::
The AlertVC:
import UIKit
class ErrorAlert: UIViewController {
var titlenote:String = ""
var message:String = ""
#IBOutlet weak var cancelBtn: UIButton!
#IBOutlet weak var messageHolder: UILabel!
#IBOutlet weak var imageHolder: UIImageView!
#IBOutlet weak var titleHolder: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.7)
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.messageHolder.text = self.message
self.titleHolder.text = self.titlenote
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func dismiss(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
}
This viewcontroller can be reuse in any vc and any number of times.
Useage Example::
let alertController = self.storyboard?.instantiateViewController(withIdentifier: "erroralert") as! ErrorAlert
alertController.titlenote = "Invalid login"
alertController.message = "Invalid facebook account."
alertController.providesPresentationContextTransitionStyle = true
alertController.definesPresentationContext = true
alertController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
alertController.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
self.present(alertController, animated: true, completion: nil)
I have made the background of the alertviewvc semitransparent by setting the alpha value.
Actual Display ::
You can make more complex alertview by this method but for reusability you have apply some logic as the button actions will be different for different viewcontroller. Example -- Sometime you can use the alertview for logout alert or sometime for submitting a form .So in both cases the action will be different so for reusability you have to write extra logic.
Another alertView::
I hope my answer will help you.:)
//
// ViewController.swift
// FunFacts
//
// Created by Alex Macleod on 4/10/14.
// Copyright (c) 2014 Alex Macleod. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var funFactLabel: UILabel!
#IBOutlet weak var funFactButton: UIButton!
#IBOutlet weak var swipeView: UIView!
// let swipeRec = UISwipeGestureRecognizer()
let factBook = FactBook()
let colorWheel = ColorWheel()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// swipeRec.addTarget(self, action: "swipedView")
// swipeView.addGestureRecognizer(swipeRec)
// swipeView.userInteractionEnabled = true
var swipeRight = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeRight.direction = UISwipeGestureRecognizerDirection.Right
self.view.addGestureRecognizer(swipeRight)
var swipeLeft = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeLeft.direction = UISwipeGestureRecognizerDirection.Left
self.view.addGestureRecognizer(swipeLeft)
var swipeDown = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeDown.direction = UISwipeGestureRecognizerDirection.Down
self.view.addGestureRecognizer(swipeDown)
var swipeUp = UISwipeGestureRecognizer(target: self, action: "respondToSwipeGesture:")
swipeUp.direction = UISwipeGestureRecognizerDirection.Up
self.view.addGestureRecognizer(swipeUp)
funFactLabel.text = factBook.randomFact()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func respondToSwipeGesture(gesture: UIGestureRecognizer) {
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
switch swipeGesture.direction {
case UISwipeGestureRecognizerDirection.Right:
// swipedAlertViewRight()
self.performSegueWithIdentifier("segueSwipeRight", sender: nil)
case UISwipeGestureRecognizerDirection.Left:
// swipedAlertViewLeft()
swipedLeft()
case UISwipeGestureRecognizerDirection.Down:
var randomColor = colorWheel.randomColor()
view.backgroundColor = randomColor
funFactButton.tintColor = randomColor
funFactLabel.text = factBook.randomFact()
case UISwipeGestureRecognizerDirection.Up:
var randomColor = colorWheel.randomColor()
view.backgroundColor = randomColor
funFactButton.tintColor = randomColor
funFactLabel.text = factBook.randomFact()
default:
break
}
}
}
func swipedLeft() {
self.performSegueWithIdentifier("segueSwipeLeft", sender: nil)
}
// func swipedAlertViewRight(){
// let tapAlert = UIAlertController(title: "Swiped", message: "You just swiped right", preferredStyle: UIAlertControllerStyle.Alert)
// tapAlert.addAction(UIAlertAction(title: "OK", style: .Destructive, handler: nil))
// self.presentViewController(tapAlert, animated: true, completion: nil)
// }
//
// func swipedAlertViewLeft(){
// let tapAlert = UIAlertController(title: "Swiped", message: "You just swiped left", preferredStyle: UIAlertControllerStyle.Alert)
// tapAlert.addAction(UIAlertAction(title: "OK", style: .Destructive, handler: nil))
// self.presentViewController(tapAlert, animated: true, completion: nil)
// }
#IBAction func showFunFact() {
var randomColor = colorWheel.randomColor()
view.backgroundColor = randomColor
funFactButton.tintColor = randomColor
funFactLabel.text = factBook.randomFact()
}
}
So I swipe left and I it takes me to a new viewViewcontroller, I swipe right it takes me to another blank view controller. How do I tell these blank view controllers to segue back to the main view controller?
Do you have a navigation bar in that view controller?
If you do, then you can simply do:
self.navigationController.popViewControllerAnimated(YES)
If you do not, then you simply need to
self.performSegueWithIdentifier("segueSwipeLeft", sender: nil)
(which is to say, perform a segue back to the view controller you came from). Segues are not necessarily a push and pop thing.
Segues create instances of their view controllers. This makes sense when moving in a forward direction, but if you "performSegueWithIdentifier" when moving in a backward direction, it's likely you're not returning to the previous view controller, but rather you're creating and presenting a new instance of the previous view controller.
For example, let's say you two view controllers, A and B, and A has a text field on it that has a value specified by the user. Then you segue to B. Then you use a standard segue back to A. The text won't be in the text field on A because you're looking at a new instance of A, not the original instance of that view controller.
If you want to back-up, there are Unwind segues, which are a special kind of segue to return you to a previous instance. They are rigged up to the green "exit" button at the top of your scene in the storyboard editor. Unwind segues (sometimes called Exit Segues) are interesting because they let you unwind not just to the previous view controller, but all the way back through a deep stack of view controllers, and as part of the unwind they can call different methods on the destination view controller, such as indicating that a Cancel or Save button was tapped on the source view controller.
Programatically, if your view controller was presented modally, you can also use dismissViewController:animated:completion: to back up.