I am trying to present QLPreviewController as one of the Tab in a Tabview contorller. It able to show PDF properly but pencilkit/Markup not shown
Reason: i want to show PencilKit to draw on the PDF and save it (other type of files from local or network also hence QL), also capture highlights from PDF(others)
Xcode 12.3
Swift 5
iOS/iPAD: 14
problem:
Q1) Unable to view Markup or edit button at the stop of PDF files. Tried hiding tab bar and subviews are always nil. Any help is appreciated.
Q2) Is it possible to override Highlight sector/function in menuItem (on PDF i see nice options so want to use it however want to save the text selection)?
class QLPreviewController2: QLPreviewController {
var toolbars: [UIView] = []
var observations : [NSKeyValueObservation] = []
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
}
extension QLPreviewController2: QLPreviewControllerDelegate {
func previewController(_ controller: QLPreviewController, editingModeFor previewItem: QLPreviewItem) -> QLPreviewItemEditingMode {
return .updateContents
}
func previewController(_ controller: QLPreviewController, shouldOpen url: URL, for item: QLPreviewItem) -> Bool {
return true
}
func previewController(_ controller: QLPreviewController, didUpdateContentsOf previewItem: QLPreviewItem) {
}
func previewController(_: QLPreviewController, didSaveEditedCopyOf: QLPreviewItem, at: URL) {
}
}
And my TabVC is
class TabVC: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let viewcontollers = viewControllers else {
return
}
let item1 = QLPreviewController2()
item1.dataSource = self
// self.navigationItem.rightBarButtonItems?.append(item1.editButtonItem) //did not work
item1.isEditing = true //did not work
item1.setEditing(true, animated: true)//did not work
let icon1 = UITabBarItem(title: "Title", image: UIImage(named: "doc"), selectedImage: UIImage(named: "doc"))
item1.tabBarItem = icon1
self.viewControllers!.append(item1)
// navigationItem.leftBarButtonItems?.append(item1.editButtonItem) //did not work
}
//Delegate methods
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
print("Should select viewController: \(viewController.title ?? "") ?")
return true;
}
}
extension TabVC: QLPreviewControllerDataSource {
func numberOfPreviewItems(in: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
print("2")
let fileUrl = Bundle.main.url(forResource: "some", withExtension: ".pdf", subdirectory: nil, localization: nil)
let filePreview = PreviewItem2()
filePreview.previewItemURL = fileUrl
return filePreview
}
}
For solving first problem you should embed your QLPreviewController into the UINavigationController and this will consequently show UINavigationBar over QLPreviewController along with bar button items. Then you must append this UINavigationController instance to viewControllers instance property of the UITabBarController.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//...
let item1 = QLPreviewController2()
let nc = UINavigationController(rootViewController: item1)
//...
self.viewControllers!.append(nc)
}
Second problem is hard to define because of your english, but I'm guessing you want some custom functionality which can't be achieved by QuickLook framework.
Related
I am using QLPreviewController to display images in my SwiftUI app. However, I've noticed that the swipe-to-dismiss that comes built-in when presenting QLPreviewController modally in UIKit doesn't work when presenting it using .fullScreenCover in SwiftUI. Also, when presenting modally in UIKit, it adds a UINavigationController around the preview and this doesn't happen when presented from SwiftUI as well.
To illustrate, here is code to display an image using QLPreviewController in UIKit. Please note that this is not code from my app, I am just including this to show how QLPreviewController works from UIKit. My app is 100% SwuftUI:
import UIKit
import QuickLook
class ViewController: UIViewController {
let button = UIButton(type: .system)
let url = Bundle.main.url(forResource: "foo", withExtension: "jpg")!
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("Preview", for: .normal)
self.view.addSubview(button)
button.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
button.frame = CGRect(x: (view.frame.size.width - 150)/2, y: (view.frame.size.height - 44)/2, width: 150, height: 44)
}
#IBAction func buttonTapped(_ button: UIButton) {
let previewController = QLPreviewController()
previewController.dataSource = self
present(previewController, animated: true)
}
}
extension ViewController : QLPreviewControllerDataSource {
#objc func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return url as QLPreviewItem
}
}
And here is code doing the same thing in SwiftUI using .fullScreenCover:
import Foundation
import SwiftUI
import QuickLook
struct ContentViewSimple: View {
#StateObject private var previewURL = URLContainer()
var body: some View {
VStack {
Button("Preview", action: {
previewURL.url = Bundle.main.url(forResource: "foo", withExtension: "jpg")
})
}
.fullScreenCover(item: $previewURL.url, content: { url in
QuickLookPreviewSimple(url: url)
})
}
}
class URLContainer : ObservableObject {
#Published var url : URL?
}
struct QuickLookPreviewSimple: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> QLPreviewController {
let controller = QLPreviewController()
controller.dataSource = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: QLPreviewController, context: Context) {
}
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
class Coordinator : NSObject, QLPreviewControllerDataSource {
let parent: QuickLookPreviewSimple
init(parent: QuickLookPreviewSimple) {
self.parent = parent
}
#objc func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return parent.url as QLPreviewItem
}
}
}
As stated previously, the UIKit code presents the image wrapped in a UINavigationController and when you swipe down on the image, the view controller is dismissed. In the SwiftUI code, there is no UINavigationController and swiping down does nothing.
Some things I have tried:
I can wrap the QLPreviewController in a UINavigationController in my SwiftUI code, and that helps to add the toolbar and toolbar buttons and allows me to add a Done button to dismiss, but the swipe down still doesn't work.
I can use .sheet instead of .fullScreenCover, and you can swipe on the navigation bar or toolbar to dismiss, but still swiping down on the image doesn't do anything.
I can use .quickLookPreview in the SwiftUI code instead of .fullScreenCover and that is an easier way to display my image in QLPreviewController and it does add the UINavigationBar, but swiping down also doesn't work.
So is this just an oversight and yet another thing broken in SwiftUI that Apple needs to fix, or is there a way to work around this limitation to allow swipe to dismiss to work on QLPreviewController in SwiftUI? Thanks.
After working on this for a while, I have come up with an acceptable workaround until this is fixed in SwiftUI (I filed feedback with Apple on it).
Instead of using QLPreviewController in a UIViewControllerRepresentable, I am creating a new UIViewController UIViewControllerRepresentable that wraps a QLPreviewController using UIKit present() and dismisses itself when the QLPreviewController is dismissed.
Here is the updated sample code:
struct QuickLookPreviewRep: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: Context) -> some UIViewController {
return QuickLookWrapper(url: url)
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
class QuickLookWrapper : UIViewController, QLPreviewControllerDataSource, QLPreviewControllerDelegate {
var url: URL?
var qlController : QLPreviewController?
required init?(coder: NSCoder) {
fatalError("no supported")
}
init(url: URL) {
self.url = url
super.init(nibName: nil, bundle: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if self.qlController == nil {
self.qlController = QLPreviewController()
self.qlController?.dataSource = self
self.qlController?.delegate = self
self.present(self.qlController!, animated: true)
}
}
#objc func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return url! as any QLPreviewItem
}
func previewControllerWillDismiss(_ controller: QLPreviewController) {
dismiss(animated: true)
}
}
I am fairly new to iOS and am a bit stuck on this issue. The problem is that when I rotate the device from landscape to portrait in SFSafariViewcontroller the DONE button gets pushed up. See image here:
I thought about maybe recalculating the height and width in viewWillTransistion but I am honestly not sure what to do. The App is portrait only and only this ViewController should be rotated.
My Safari View Controller:
extension SFSafariViewController {
convenience init(url URL: URL, entersReaderIfAvailable: Bool) {
let configuration = SFSafariViewController.Configuration()
configuration.entersReaderIfAvailable = entersReaderIfAvailable
self.init(url: URL, configuration: configuration)
}
}
public class MySafariViewController: SFSafariViewController {
public var isIdleTimerDisabled: Bool = false
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if size.height > size.width {
Logging.debug("We are in portrait mode currently. Transitioning to landscape")
} else {
Logging.debug("We are in landscape mode currently. Transitioning to portrait")
}
}
public override func viewDidLoad() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.allowRotateAlways = true
self.view.insetsLayoutMarginsFromSafeArea = true
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if isIdleTimerDisabled {
UIApplication.shared.isIdleTimerDisabled = true
}
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
public override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isIdleTimerDisabled { // for the case if it's been true at the startup
UIApplication.shared.isIdleTimerDisabled = false
}
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
appDelegate.allowRotateAlways = false
}
public override var shouldAutorotate: Bool {
return true
}
}
I am allowing rotation by setting the allowRotateAlways variabel to true. The variabel is used in AppDelegate in the func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {} function to change the orientation. So orientation change works i.e going from portrait to landscape and vice versa, but I am unsure why I get these side effects.
The Problem
I have a problem with my split view. It works fine on iPhone and iPad simulators, but on the iPhone 6+ I lose the navigation bar after rotating the device. Here's what happens on the 6+ simulator:
I start the app and it presents a + button in the navigation bar. I tap this button.
It loads a view controller over the existing view. A navigation bar, as expected, is visible with a working back button.
I turn the device horizontally. As intended the new controller appears in the Master section, with an empty detail section on the right. Unfortunately the navigation bar dissapears.
When I turn the device vertically the navigation bar does not reappear.
In fact when I turn the device horizontally it seems the navigation controller is removed from the stack (I've observed this from outputting the contents of splitViewContoller.viewControllers).
My Code
The test application is simply the Master Detail template with a few modifications.
I've added a new "Add Item" controller and then created a show segue from the Master view's "+" button. The "Add Item" controller is blank, just a blue background.
The DetailViewController has a timerStarted boolean value that is true when the detail view is being used and false when it isn't. The master view is hidden when the detail is in use and displayed when it isn't.
Here's the relevant code (there's nothing interesting in AppDelegate as it's no longer a split view delegate, and MasterViewController has no interaction as the button works via the storyboard)
DetailViewController
import UIKit
class DetailViewController: UIViewController, UISplitViewControllerDelegate {
#IBOutlet weak var detailDescriptionLabel: UILabel!
var collapseDetailViewController = true
var detailItem: AnyObject? {
didSet {
self.configureView()
}
}
var timerStarted: Bool = false {
didSet {
self.changeTimerStatus()
}
}
func configureView() {
if let detail: AnyObject = self.detailItem {
if let label = self.detailDescriptionLabel {
label.text = detail.description
self.timerStarted = true
}
}
}
func changeTimerStatus() {
if self.timerStarted {
if splitViewController!.collapsed == false {
UIView.animateWithDuration(0.3, animations: {
self.splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryHidden
})
}
collapseDetailViewController = false
} else {
if splitViewController!.collapsed {
self.splitViewController?.viewControllers[0].popToRootViewControllerAnimated(true)
} else {
UIView.animateWithDuration(0.3, animations: {
self.splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible
})
}
collapseDetailViewController = true
}
}
override func viewDidLoad() {
super.viewDidLoad()
splitViewController?.delegate = self
self.disabledScreen.hidden = false
self.view.bringSubviewToFront(disabledScreen)
self.configureView()
}
override func viewWillAppear(animated: Bool) {
if splitViewController!.collapsed == false && self.timerStarted == false {
splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible
}
}
#IBAction func closeButton(sender: AnyObject) {
self.timerStarted = false
}
func primaryViewControllerForExpandingSplitViewController(splitViewController: UISplitViewController) -> UIViewController? {
if timerStarted == true {
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryHidden
} else {
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible
}
return nil
}
func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController!, ontoPrimaryViewController primaryViewController: UIViewController!) -> Bool {
return collapseDetailViewController
}
}
AddItemViewController
import UIKit
class AddItemViewController: UIViewController, UISplitViewControllerDelegate {
var collapseDetailViewController = false
override func viewDidLoad() {
super.viewDidLoad()
self.splitViewController?.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidAppear(animated: Bool) {
self.splitViewController?.delegate = self
self.collapseDetailViewController = false
}
override func viewWillDisappear(animated: Bool) {
self.splitViewController?.delegate = nil
self.collapseDetailViewController = true
}
func primaryViewControllerForExpandingSplitViewController(splitViewController: UISplitViewController) -> UIViewController? {
return self
}
func primaryViewControllerForCollapsingSplitViewController(splitViewController: UISplitViewController) -> UIViewController? {
return nil
}
func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController!, ontoPrimaryViewController primaryViewController: UIViewController!) -> Bool {
return collapseDetailViewController
}
}
I'd be grateful for any suggestions.
I have found the answer. I read a article which I had originally missed because it focuses on changing the detail view rather than the master. As it turns out, the split view works better if I just manage the detail and then the master will take care of itself. Since I never want to change the detail I can simply add the following to my split view delegate:
func splitViewController(splitViewController: UISplitViewController, separateSecondaryViewControllerFromPrimaryViewController primaryViewController: UIViewController!) -> UIViewController? {
return (UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("detailView") as! UIViewController)
}
Once this is done, I no longer lose the navigation bar.
I'm following this tutorial: http://swiftiostutorials.com/ios-tutorial-using-uipageviewcontroller-create-content-slider-objective-cswift/ to create an app that shows multiple sliders.
Even though i've got this tutorial to work, This example only changes an image based on those that are stored in an array.
How can I get it to load ViewControllers instead of images
I have 4 ViewControllers:
ViewController1
ViewController2
ViewController3
ViewController4
I would like slide one to show ViewController1 and slide2 to load ViewController2 etc....
Here is my main ViewController:
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
// MARK: - Variables
private var pageViewController: UIPageViewController?
// Initialize it right away here
private let contentImages = ["nature_pic_1.png",
"nature_pic_2.png",
"nature_pic_3.png",
"nature_pic_4.png"];
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
createPageViewController()
setupPageControl()
}
private func createPageViewController() {
let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as UIPageViewController
pageController.dataSource = self
if contentImages.count > 0 {
let firstController = getItemController(0)!
let startingViewControllers: NSArray = [firstController]
pageController.setViewControllers(startingViewControllers, direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController!.didMoveToParentViewController(self)
}
private func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.grayColor()
appearance.currentPageIndicatorTintColor = UIColor.whiteColor()
appearance.backgroundColor = UIColor.darkGrayColor()
}
// MARK: - UIPageViewControllerDataSource
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
let itemController = viewController as PageItemController
if itemController.itemIndex > 0 {
return getItemController(itemController.itemIndex-1)
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let itemController = viewController as PageItemController
if itemController.itemIndex+1 < contentImages.count {
return getItemController(itemController.itemIndex+1)
}
return nil
}
private func getItemController(itemIndex: Int) -> PageItemController? {
if itemIndex < contentImages.count {
let pageItemController = self.storyboard!.instantiateViewControllerWithIdentifier("ItemController") as PageItemController
pageItemController.itemIndex = itemIndex
pageItemController.imageName = contentImages[itemIndex]
return pageItemController
}
return nil
}
// MARK: - Page Indicator
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return contentImages.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
and here is my PageItemController:
import UIKit
class PageItemController: UIViewController {
// MARK: - Variables
var itemIndex: Int = 0
var imageName: String = "" {
didSet {
if let imageView = contentImageView {
imageView.image = UIImage(named: imageName)
}
}
}
#IBOutlet var contentImageView: UIImageView?
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
contentImageView!.image = UIImage(named: imageName)
}
}
I'm new to Swift/iOS Development and really trying to get into it by developing. Thank you in advance for your answers :)
EDIT: To Make Question Clear
How do I make it so that there is an array of view controllers that correspond to the slide left/right of the UIPageViewController?
So when I swipe left on ViewController1 - the UIViewController2 is loaded and reverse for swipe right.
Assuming you have view controllers 1-4 defined in the same storyboard as your UIPageViewController, and you have their Storyboard IDs set as ViewController0, ViewController1, and et cetera, then create a method to populate your view controller array and call it in your viewDidLoad() before calling createPageViewController()
override func viewDidLoad() {
super.viewDidLoad()
populateControllersArray()
createPageViewController()
setupPageControl()
}
Implement the method like so:
var controllers = [PageItemController]()
func populateControllersArray() {
for i in 0...3 {
let controller = storyboard!.instantiateViewControllerWithIdentifier("ViewController\(i)") as PageItemController
controller.itemIndex = i
controllers.append(controller)
}
}
And define your createPageViewController() as the following
private func createPageViewController() {
let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as UIPageViewController
pageController.dataSource = self
if !controllers.isEmpty {
pageController.setViewControllers([controllers[0]], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController!.didMoveToParentViewController(self)
}
then in your two delegate before and after methods:
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
if let controller = viewController as? PageItemController {
if controller.itemIndex > 0 {
return controllers[controller.itemIndex - 1]
}
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
if let controller = viewController as? PageItemController {
if controller.itemIndex < controllers.count - 1 {
return controllers[controller.itemIndex + 1]
}
}
return nil
}
And in the count method
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return controllers.count
}
In fact, you can populate controllers with any view controllers you want to display, just set their class as PageItemController in storyboard (in order to have index property).
Or you can set each view controller as it's own class, and use runtime property getting and setting.
Use controller.valueForKey("itemIndex") as Int in the before and after method instead of controller.itemIndex
Use controller.setValue(i, forKey: "itemIndex") instead of controller.itemIndex = i in populateControllersArray().
Just ensure that each controller class has the Int property itemIndex, or your application will crash.
To bring it all together in your code, do the following:
import UIKit
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource {
// MARK: - Variables
private var pageViewController: UIPageViewController?
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
populateControllersArray()
createPageViewController()
setupPageControl()
}
var controllers = [PageItemController]()
func populateControllersArray() {
for i in 0...3 {
let controller = storyboard!.instantiateViewControllerWithIdentifier("ViewController\(i)") as PageItemController
controller.itemIndex = i
controllers.append(controller)
}
}
private func createPageViewController() {
let pageController = self.storyboard!.instantiateViewControllerWithIdentifier("PageController") as UIPageViewController
pageController.dataSource = self
if !controllers.isEmpty {
pageController.setViewControllers([controllers[0]], direction: UIPageViewControllerNavigationDirection.Forward, animated: false, completion: nil)
}
pageViewController = pageController
addChildViewController(pageViewController!)
self.view.addSubview(pageViewController!.view)
pageViewController!.didMoveToParentViewController(self)
}
private func setupPageControl() {
let appearance = UIPageControl.appearance()
appearance.pageIndicatorTintColor = UIColor.grayColor()
appearance.currentPageIndicatorTintColor = UIColor.whiteColor()
appearance.backgroundColor = UIColor.darkGrayColor()
}
// MARK: - UIPageViewControllerDataSource
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
if let controller = viewController as? PageItemController {
if controller.itemIndex > 0 {
return controllers[controller.itemIndex - 1]
}
}
return nil
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
if let controller = viewController as? PageItemController {
if controller.itemIndex < controllers.count - 1 {
return controllers[controller.itemIndex + 1]
}
}
return nil
}
// MARK: - Page Indicator
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return controllers.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
You do load ViewControllers for every page. every image that you show is inside it's own ViewController. That is done through:
private func getItemController(itemIndex: Int) -> PageItemController?
if every page of yours uses the same layout, there is nothing left to do here, except designing this ViewController in the Interface Builder.
If, however, every page uses a different layout and shows different data, you would first prototype/design those ViewControllers in Interface Builder.
then you would create classes for every ViewController and extend them from PageItemController. You only keep the index variable in PageItemController and move the rest of your logic to the subclasses.
import UIKit
class PageItemController: UIViewController {
// MARK: - Variables
var itemIndex: Int = 0
}
for example a viewController that holds an image
import UIKit
class PageImageViewController: PageItemController {
// MARK: - Outlets
#IBOutlet var contentImageView: UIImageView?
// MARK: - Variables
var imageName: String = "" {
didSet {
if let imageView = contentImageView {
imageView.image = UIImage(named: imageName)
}
}
}
// MARK: - View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
contentImageView!.image = UIImage(named: imageName)
}
}
finally you just change your getItemController function to return the correct ViewController for the specified index. here you either pass data to the ViewController or you just return it.
private func getItemController(itemIndex: Int) -> UIViewController? {
var vc: PageItemController? = nil
switch itemIndex {
case 0:
// show an ImageViewController and pass data if needed
vc = self.storyboard!.instantiateViewControllerWithIdentifier("ImageController") as PageImageViewController
vc.itemIndex = itemIndex
vc.imageName = "any_image_file"
case 1:
// show any other ViewController and pass data if needed
vc = self.storyboard!.instantiateViewControllerWithIdentifier("ANY_OTHERController") as ANY_OTHERController
vc.PRESENTABLE_DATA = ANY_PRESENTABLE_DATA_SOURCE
vc.itemIndex = itemIndex
case 2:
// show any other ViewController and pass data if needed
vc = self.storyboard!.instantiateViewControllerWithIdentifier("ANY_OTHERController") as ANY_OTHERController
vc.PRESENTABLE_DATA = ANY_PRESENTABLE_DATA_SOURCE
vc.itemIndex = itemIndex
}
return vc
}
Here is a great repo for this:
https://github.com/goktugyil/EZSwipeController
private func setupPageViewController() {
pageViewController = UIPageViewController(transitionStyle: UIPageViewControllerTransitionStyle.Scroll, navigationOrientation: UIPageViewControllerNavigationOrientation.Horizontal, options: nil)
pageViewController.dataSource = self
pageViewController.delegate = self
pageViewController.setViewControllers([stackPageVC[stackStartLocation]], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
pageViewController.view.frame = CGRect(x: 0, y: Constants.StatusBarHeight, width: Constants.ScreenWidth, height: Constants.ScreenHeightWithoutStatusBar)
pageViewController.view.backgroundColor = UIColor.clearColor()
addChildViewController(pageViewController)
view.addSubview(pageViewController.view)
pageViewController.didMoveToParentViewController(self)
}
I got Eric Ferreira's code above to work (Xcode 6.4). I wanted to use a Page View Controller to display two completely unique view controllers with labels that displays different data from a Realm database.
I got it to work in a test project where I used 2 storyboards, each containing a single label that is set by its own class containing the IBOutlet to the label and code setting the label text -- this adequately simulates the way I am displaying my Realm database data. Each storyboard class inherits the PageItemController so as to have the "itemIndex" variable available to it. The PageItemController in turns inherits UIViewController completing the inheritance chain.
I hope this helps someone that is seeking to use completely unique storyboards.
No matter what I do, status bar keeps coming when i open image picker and won't go away after it is dismissed. I tried various swift solutions I was able to read on this site that are supposed to be fix the problem, but it won't help at all.
Here is what I do. I subclass the picker controller:
class MyImagePickerController: UIImagePickerController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.setNeedsStatusBarAppearanceUpdate()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.setNavBar()
}
override func prefersStatusBarHidden() -> Bool {
self.setNavBar()
return true
}
override func childViewControllerForStatusBarHidden() -> UIViewController? {
return nil;
}
func setNavBar() -> Void {
self.setNavBar(65)
}
func setNavBar(height: CGFloat) -> Void {
var frame = self.navigationBar.frame;
frame.size.height = height;
self.navigationBar.frame = frame;
}
}
then I to call it from an IBoutled action:
func chooseImageFromGallery() {
var image = MyImagePickerController()
image.delegate = self
image.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
image.allowsEditing = false
self.presentViewController(image, animated: true, completion: nil)
}
Also, of course, I set the bar hidden in the Plist file. Problem is specifically when calling the picker controller.
Please answer in Swift.
Use below code to do this
import Foundation
extension UIImagePickerController {
override public func prefersStatusBarHidden() -> Bool {
return true
}
}
this is an extention(category) of UIImagePickerController and works for me.