Slimming fat viewcontroller - Barcode scanner not working anymore - ios

I have a button on my app that opens upp a barcode scanner, I can then scan an item and it prints the barcode etc.
My Viewcontroller was getting very full of codes, and i just wanted to create another class to seperate and make my code easier to read. So i put all of the barcodescanner code in the class "Partnyallen" and kept my button in class "NewViewController". I can open the barcode, but as soon as i scan an item or even if i press the "cancel" button inside, it just freezes. What could the problem be?
Thankful for any help!
import AVFoundation
import QRCodeReader
import Alamofire
class Partynallen: UIViewController, QRCodeReaderViewControllerDelegate {
lazy var readerVC: QRCodeReaderViewController = {
let builder = QRCodeReaderViewControllerBuilder {
//change object to scan and the initial position of the camera
$0.reader = QRCodeReader(metadataObjectTypes: [.qr, .ean13], captureDevicePosition: .back)
// Configure the view controller (optional)
$0.showTorchButton = true
$0.showSwitchCameraButton = false
$0.showCancelButton = true
$0.showOverlayView = true //shows the square area of the QRCode Scanner
$0.rectOfInterest = CGRect(x: 0, y: 0, width: 1, height: 1)
}
return QRCodeReaderViewController(builder: builder)
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
readerVC.delegate = self
}
func reader(_ reader: QRCodeReaderViewController, didScanResult result: QRCodeReaderResult) {
//code to be added
reader.stopScanning()
//print(result)
//print(result.value)
//barcode = result.value
//Apifetch(code: "URL")
dismiss(animated: true, completion: nil)
}
func readerDidCancel(_ reader: QRCodeReaderViewController) {
//code to be added
reader.stopScanning()
dismiss(animated: true, completion: nil)
}
}
class NewViewController: UIViewController {
var partynallen: Partynallen?
override func viewDidLoad() {
super.viewDidLoad()
partynallen = Partynallen() // DONT KNOW IF THIS IS CORRECT?
}
#IBAction func scan(_ sender: UIButton) {
partynallen.readerVC.modalPresentationStyle = .formSheet
present(partynallen.readerVC, animated: true)
}
}

You got 3 ViewControllers NewViewController, QRCodeReaderViewController and Partynallen. But actually you never present Partynallen and just use it as a delegate for the scanner, instead you only present the readerVC. So, when readerDidCancel(:) is called, you try to dismiss Partynallen, which isn’t presented.
Using a ViewController that only acts as the scanner-delegate seems to be a bit too much. You could maybe remove the UIViewController base-class (if it isn‘t required by the scanner-delegate-protocol). And because the delegate-functions, e.g readerDidCancel(:), provide the scanner-vc, you could just use that vc to dismiss itself.

Related

Adding a UIView/UIGestureRecognizer to the presented view in a UIPresentationController

I am trying to recreate the bottom drawer functionality seen in Maps or Siri Shortcuts by using a UIPresentationController by having it recognise user input and updating the frameOfPresentedViewInContainerView accordingly. However I want this mechanism to work independently of the presented UIViewController as much as possible so I'm trying to have the presentation controller add a handle area above the view. Ideally the view of the presented controller and the handle are should both recognise user input.
This works for the presented view, however any view I add to it responds to no UIGestureRecognizer at all. Am I missing something?
class PresentationController: UIPresentationController {
private let handleArea: UIView = UIView()
override var frameOfPresentedViewInContainerView: CGRect {
// Return some frame for now
return CGRect(x: 0, y: 250, width: containerView!.frame.width, height: 500)
}
override func presentationTransitionWillBegin() {
// Unwrap presented view
guard let presentedView = self.presentedView else {
return
}
// Set color
self.handleArea.backgroundColor = UIColor.green
// Add to view hierachy
presentedView.addSubview(self.handleArea)
// Set constraints
self.handleArea.trailingAnchor.constraint(equalTo: presentedView.trailingAnchor).isActive = true
self.handleArea.leadingAnchor.constraint(equalTo: presentedView.leadingAnchor).isActive = true
self.handleArea.bottomAnchor.constraint(equalTo: presentedView.topAnchor).isActive = true
self.handleArea.heightAnchor.constraint(equalToConstant: 56).isActive = true
self.handleArea.translatesAutoresizingMaskIntoConstraints = false
// These don't help
self.handleArea.isUserInteractionEnabled = true
presentedView.isUserInteractionEnabled = true
presentedView.bringSubviewToFront(self.handleArea)
}
override func presentationTransitionDidEnd(_ completed: Bool) {
if completed {
// Add gesture recognizer
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.onHandleAreaTapped(sender:)))
self.handleArea.addGestureRecognizer(tapGestureRecognizer)
}
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
// Remove subview
self.handleArea.removeFromSuperview()
}
// MARK: - Responder
#objc private func onHandleAreaTapped(sender: UITapGestureRecognizer) {
print("tap") // No output
}
}
I managed to solve it by adding both the handle area and the view of the presentedViewController to a custom view and then overriding the presentedView property and returning my custom view.

Swift: Unexpectedly found "nil" when trying to change property of objects in a function called from another swift file

So, on my Storyboard I have an UIViewController with a Container View which it has an UITableViewController as embed. That's how it looks like: https://i.imgur.com/0yBWtbG.png
But now I want to set a picture taken by the camera, but the problem is: The UIImageView is inside the UITableViewController and the button for opening the camera is inside the UIViewController (you can see the camera icon on the screenshot I uploaded).
So, I tried this:
Code for the UIViewController:
#IBAction func openCamera(_ sender: UIButton) {
let image = UIImagePickerController()
image.delegate = self
image.sourceType = UIImagePickerController.SourceType.camera
image.allowsEditing = false
self.present(image, animated: true)
{
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
{
let info = convertFromUIImagePickerControllerInfoKeyDictionary(info)
if let image = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.originalImage)] as? UIImage
{
takenPhoto = image //takenPhoto is a global variable
let CIV = CreateIssue()
CIV.showImage()
}
else
{
}
self.dismiss(animated: true, completion: nil)
}
fileprivate func convertFromUIImagePickerControllerInfoKeyDictionary(_ input: [UIImagePickerController.InfoKey: Any]) -> [String: Any] {
return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)})
}
fileprivate func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String {
return input.rawValue
}
}
Code for the UITableViewController:
public func showPicture() {
ivPicture.isHidden = false //here I get Unexpectedly found nil after taking a picture from the camera
ivPicture.image = takenPhoto
}
However, if I try to call the function in the ViewDidLoad, it works fine:
override func viewDidLoad() {
showPicture()
}
The last few days, I posted a similar issue but I was trying to change a variable from another swift file through segues, and I was told to use the prepare for segue method but in this case I am just calling a function of another swift file and the obejcts belong to the same swift file. Would I need to use the prepare for segue method, too? Or what am I doing wrong?
Thank you in advance
One thing to keep in mind - A UIView, Tabbleview, and it's cells are all different classes / objects. They don't have any natural relation between one another even though they appear on the same user interface. A button in a tableview cell is a member of that Cell's class rather than the Table or the View which contains that Table.
One question - do you need to have this button in a Tableview cell or can it simply exist as part of your UIView? It's unclear from your example if you need to repeat these cells / have multiple instances of them (the typically UITableViewCell use case) or if a single cell is all you are looking for.
You create an instance of the child vc
let CIV = CreateIssue()
CIV.showImage()
that not loaded from storyboard so all outlets are nil , so replace this
let CIV = CreateIssue()
with
let CIV = self.children[0] as! CreateIssue

How to disable selection of tab bar items - swift

I have an application that has two UIViewcontroller embedded in a UITabBarcontroller. When I am in UIViewController-1, i would like to press a button that disables all item selection of the tab bar. My effort is below but I am not sure how to complete the code ...
When I am in the 'Folders' UIViewController I would like to disable the selection of any tab bar item:
class Folders: UIViewController, UITableViewDataSource, UITableViewDelegate{
...
// DISABLE TAB BAR ITEMS
func disable (){
let tabBarItemsArray = self.tabBarController?.tabBar.items
tabBarItemsArray[0].enabled = false // THIS BIT OF CODE IS NOT RECOGNIZED BY XCODE
}
...
}
tabBarItemsArray is optional, its type is [UITabBarItem]?.
You could initially force unwrap it: tabBarItemsArray![0], but the right way is to use if let construct:
if let tabBarItemsArray = tabBarController.tabBar.items {
tabBarItemsArray[0].isEnabled = false
}
or:
guard let tabBarItemsArray = tabBarController.tabBar.items else {
fatalError("Error")
}
let item = tabBarItemsArray[0]
item.isEnabled = false
You can do that using single line of code. Please check following code.
You can execute this from any controller.
self.navigationController?.tabBarController?.tabBar.items![0].isEnabled = false
Another way
You can define NotificationCenter observer to achieve. Please check following code. *In TabBar Controller file.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
NotificationCenter.default.addObserver(self, selector: #selector(disableTab(notification:)), name: Notification.Name("disableTab"), object: nil)
}
#objc func disableTab(notification: Notification) {
self.TabBarItem.isEnabled = false
}
Fire from anywhere as following...
NotificationCenter.default.post(name: Notification.Name("disableTab"), object: nil)
if you want to disable one tabbar item at once then this is for disabling the first one:
guard let tabbars = self.tabBar.items else {
return
}
tabbars[0].isEnabled = false
but if you want them all to be disabled at once then this is the one to be implemented:
self.tabBar.items?.map{$0.isEnabled = false}

How to show FullScreenSlideshowViewController on ViewDidLoad

I am new to iOS development, I have created android app where app gets sets of images (sometimes over 100 images) from urls and loads into imageView with zoomEnabled Inside ViewPager.
Now I want to create same app for iOS, I have found this lib ImageSliderShow. Problem with this is it shows fullscreen ONLY when user did tap on selected image. I have been struggling to presentFullScreenController on viewDidLoad with no lock.
I only want image to be shown in fullScreen, example:
Select Category A From CategoryVC -> Loads ImageSlideVC, gets set of images from server, show in FullScreenView.
How can i achieve this? Adding this:
slideshow.presentFullScreenController(from: self)
on viewDidLoad didn't work:
#IBOutlet var slideshow: ImageSlideshow!
let kingfisherSource = [KingfisherSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!,
KingfisherSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!,
KingfisherSource(urlString: "https://images.unsplash.com/photo-1463595373836-6e0b0a8ee322?w=1080")!]
override func viewDidLoad() {
super.viewDidLoad()
slideshow.setImageInputs(kingfisherSource)
slideshow.presentFullScreenController(from: self)
}
I dont get any error, output is same as before (shows slideshow in smallview with no zoom)
Please help
EDIT
by doing in viewDidAppear, after split second view is loaded its loads into fullscreen. First its smallview then shows in fullscreen. I think i have to do something inside setImageInputs
this is what its looks like :
open func setImageInputs(_ inputs: [InputSource]) {
self.images = inputs
self.pageControl.numberOfPages = inputs.count
// in circular mode we add dummy first and last image to enable smooth scrolling
if circular && images.count > 1 {
var scImages = [InputSource]()
if let last = images.last {
scImages.append(last)
}
scImages += images
if let first = images.first {
scImages.append(first)
}
self.scrollViewImages = scImages
} else {
self.scrollViewImages = images
}
reloadScrollView()
layoutScrollView()
layoutPageControl()
setTimerIfNeeded()
}
try to do it in viewDidAppear
#IBOutlet var slideshow: ImageSlideshow!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
slideshow.setImageInputs(kingfisherSource)
slideshow.presentFullScreenController(from: self)
}
Presenting viewController in ViewDidLoad is a bad practice.

How to transfer data between Parent and Child View Controllers

I have tried looking at answers on similar questions to this, but I am not particularly experienced and have had trouble following them, so any help would be much appreciated! My situation is as follows: when I press a button in my Parent ViewController, the following code is used to call a Child ViewController (by the way, the Child is actually a TableViewController, but it seems to work fine "thinking" it's a normal ViewController?):
controller = (storyboard?.instantiateViewController(withIdentifier: "People"))
addChildViewController(controller!)
controller?.view.frame = CGRect(x: 10, y: 200, width: 394, height: 300)
self.view.addSubview((controller?.view)!)
controller?.didMove(toParentViewController: self)
What I would then like is to transfer an array from the Parent to the Child, where it will be used as the TableView's data?
Secondly, when I select a cell from the Child's TableView, I would like the relevant information to be sent to the Parent, and for the Child to disappear.
In case it is of interest, I have managed to close the Child under different circumstances (when a click occurs in the Parent while the Child is displayed) using the following:
controller?.willMove(toParentViewController: nil)
controller?.view.removeFromSuperview()
controller?.removeFromParentViewController()
I would really appreciate any advice, even if it's a link to something which would help!
You can pass value from Parent to Child Controller like this
controller = (storyboard?.instantiateViewController(withIdentifier: "People"))
addChildViewController(controller!)
controller?.view.frame = CGRect(x: 10, y: 200, width: 394, height: 300)
controller.tableDataSource = // Pass your required value to child controller
self.view.addSubview((controller?.view)!)
controller?.didMove(toParentViewController: self)
Now you want to transfer back your select value to Parent view controller. For this purpose your have a create a Delegate in ChildController like
#protocol ChildControllerDelegate : class {
func selectedValue(Value : String)
}
After that make a variable of that delegate in ChildController like this
weak var delegate : ChildControllerDelegate?
and when in rowDidSelect method add following code
if(delegate != nil) {
delegate.selectedValue(Value :"Your selected value")
}
Now step when you are going to show ChildController from ParentController at that time you have to set that delegate object to ParentController like this
controller = (storyboard?.instantiateViewController(withIdentifier: "People"))
addChildViewController(controller!)
controller?.view.frame = CGRect(x: 10, y: 200, width: 394, height: 300)
controller.delegate = self
self.view.addSubview((controller?.view)!)
controller?.didMove(toParentViewController: self)
and after that just implement the delegate method in ParentController like that
func selectedValue(Value : String) {
// you select val
}
Try This.
First Create Public Method For Add And Remove childVC.
For Add childVC.
public class func openChildViewController(parentVC:UIViewController, with childVC:UIViewController){
parentVC.addChildViewController(childVC)
childVC.view.frame = parentVC.view.frame
parentVC.view.addSubview(childVC.view)
parentVC.didMove(toParentViewController: childVC)
}
For Remove childVC.
public class func removeChildViewController(childVC:UIViewController){
childVC.willMove(toParentViewController: nil)
childVC.view.removeFromSuperview()
childVC.removeFromParentViewController()
}
Use Above Method.
1.ParentVC.swift
class ParentVC: UIViewController , ChildVCDelegate {
var arrType = NSMutableArray()
//add ChildVC
#IBAction func btnAddChildVC(_ sender: UIButton) {
let ChildVC = self.storyboard?.instantiateViewController(withIdentifier: "ChildVC") as! ChildVC
PickerVC.arrPass = arrType //for data passing create any object in ChildVC for ex. arrPass is NSMutableArray
ChildVC.delegate = self
openChildViewController(parentVC: self, with: ChildVC)
}
// MARK: ChildVC Delegate
func SetSelectedPickerValue(strSelectOption: String) {
print(strSelectOption)
}
}
}
2.ChildVC.swift
class ChildVC: UIViewController{
// MARK: Variable for ParentVCData Passing
var arrPass = NSMutableArray()
override func viewWillAppear(_ animated: Bool) {
print(arrPass)
}
//Remove ChildVC
#IBAction func btnRemoveChildVC(_ sender: UIButton) {
self.delegate?.SetSelectedPickerValue!(strSelectOption: “any String you pass ChildVC To ParentVC”)
removeChildViewController(childVC: self)
}
}
// MARK: Create Delegate Method
#objc protocol ChildVCDelegate{
#objc optional func SetSelectedPickerValue(strSelectOption:String)
}
You can:
Cast the controller to the appropriate class for the child view controller (I'm using ChildViewController below, but hopefully you have a more descriptive name); and
Pass the array (which I guessed you might have called people, but use whatever your array names are in these two respective view controllers) from the current view controller (the parent) to this new child view controller.
Thus:
let child = storyboard!.instantiateViewController(withIdentifier: "People") as! ChildViewController
addChildViewController(child)
child.people = people
child.view.frame = ...
view.addSubview(child.view)
child.didMove(toParentViewController: self)
Personally, I wouldn't hard code the child coordinates like you did in your original question. I'd set translatesAutoresizingMaskIntoConstraints to false and then add the appropriate leading/trailing/top/bottom constraints, but that's up to you. It was just too painful to see hardcoded coordinates in your example.

Resources