I'm trying to get my head around iOS development (Swift + Xcode 6), and I'm trying to figure out how what I do in the Interface Builder relates to my code.
What I've done is created PageViewController : UIPageViewController which, in its viewDidLoad method, calls setViewControllers to add a single view controller, an instance of Page11ViewController : UIViewController.
Thanks to a helpful answer on a recent question, I now know that PageViewController and Page1ViewController are being created successfully. However, I am trying to design Page1ViewController in IB, and what's being rendered doesn't reflect the work I'm doing in IB. In my storyboard, I have the following:
So "Page 1 View Controller Scene" contains a view that contains a label ("Page 1"). I have made "Page 1 View Controller" an instance of Page1ViewController by setting its class in the Identity Inspector for that view controller:
But when I run the app, I don't see my white view with a "Page 1" label; instead I see a blank red view. Why red? Because I did this:
class Page1ViewController : UIViewController {
#IBOutlet var mainView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.redColor()
println("Page 1 loaded")
println("Subviews: \(view.subviews.count)")
}
}
So I know that code is being executed, and the background is being set there...and the subview count printed out is 0...so I know that the view I'm constructing in my IB Storyboard is not the view that's actually getting drawn.
Here is the code from PageViewController that adds Page1ViewController:
class PageViewController : UIPageViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println("Page view controller loaded")
let page1vc = Page1ViewController()
setViewControllers([page1vc],
direction: UIPageViewControllerNavigationDirection.Forward,
animated: true, completion: nil)
}
}
How do I attach the view I'm creating in IB to the code?
Your problem is that you are instantiating an empty Page1ViewController, rather than loading it from your storyboard so it isn't connected to any of the objects you defined in the scene.
You should use -
class PageViewController : UIPageViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println("Page view controller loaded")
let storyboard = UIStoryboard(name:"MainStoryboard" bundle:nil);
let page1vc = storyboard.instantiateViewControllerWithIdentifier("page1ViewController") as Page1ViewController // Check the Storyboard ID for your scene in the storyboard
setViewControllers([page1vc],
direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
}
}
Related
I'm building an app where two view controllers share a UIView subclass as the main source of UI. It works perfectly when the app is starting, but if I navigate away from the initial view, and return to it, all of the UI is lost. What do I need to do to preserve the views UI post-navigation?
My app flow is: MainView -> TableView -> DetailView
Just going from Main to Table to Main itself makes the UI vanish.
(rank isn't 10 yet, so here's a link to view: https://gfycat.com/enormousanchoredindochinesetiger)
What I do is load the UI in the UIView class through layoutSubviews, and in the UIViewControllers I set the instantiate the class, UI in the loadViews method by saying view = viewClass. I've tried adding this (view = viewClass) to viewWillAppear() as well, but it does nothing.
I've also tried creating two unique view classes in case instantiating was a problem. It didn't change anything.
ViewController:
override func loadView() {
super.loadView()
view = baseView
view.backgroundColor = .white
self.navigationController?.isNavigationBarHidden = true
requestLaunchData()
setButtonTargets()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = true
view = baseView
}
//How I push to the next view
#objc func upcomingButtonTapped() {
let vc = TableViewController()
navigationController?.pushViewController(vc, animated: true)
vc.upcomingLaunches = upcomingLaunches
}
UIView:
class BaseView: UIView {
//Lots of labels and buttons instantiated
override func layoutSubviews() {
super.layoutSubviews()
setUI() //adding subviews
}
//Layout configurations
}
Before it was this structure, I had all the UI (labels, buttons, a map) directly created and configured in each ViewController, which made both massive. But, it also worked.
I solved it after a night's rest.
So here's how you need to use a custom UIView class as your ViewController's view:
class YourView: UIView {
//Create your properties, views, etc.
let exampleView = UIView()
override layoutSubviews(){
super.layoutSubviews()
addSubview(exampleView)
//Add layouts, etc.
}
And then in your ViewController, in either viewDidLoad, or loadViews (like me here):
let customView = YourView()
override func loadView() {
super.loadView()
view = customView //Sets the entire view to all the UI you created in the custom class
}
The FATAL mistake I made was this:
override layoutSubviews(){
super.layoutSubviews()
if let sView = superview { //This gives you frame size and such
sView.addSubview(exampleView)
}
}
This sets the UI's memory to the SuperView, which gets lost the moment you leave that view, because of view = customView. So my controller was rendering view = customView, which was empty, because all the UI was set to the superView which was superseded by customView.
I hope that helps anyone trying to use this same architecture in the future.
Is there a way to reposition the UIKit tab bar vertically? Like the
gmail app navigation
This question has been asked quite a few times. None of the solutions I found seem to work for me. I’m looking for a solution from scratch in Swift, no third party libs.
Just like what #rmaddy has commented, you will need to create a customised tab and place it in a view as shown below.
FYI, you can easily build a customised tab bar by using buttons.
Once clicked, you just need to add the new controller as a subview by using the extension below.
extension UIViewController {
func add(parentViewController: UIView, childViewController: UIViewController) {
// Add Child View Controller
addChild(childViewController)
// Add Child View as Subview
parentViewController.addSubview(childViewController.view)
// Configure Child View
childViewController.view.frame = parentViewController.bounds
// Notify Child View Controller
childViewController.didMove(toParent: self)
}
}
So from your main view controller, it should look something like this.
class MainController: UIViewController {
// Outlets
#IBOutlet weak var mainView: UIView!
// Lazy loaded controller
private lazy var tab1Controller: Tab1Controller = {
// Instantiate View Controller
let tab1Controller = UIStoryboard(name: "Tab1Controller", bundle: nil).instantiateViewController(withIdentifier: "Tab1") as! Tab1Controller
return tab1Controller
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// MARK: - Tab Bar
#IBAction func Tab1TouchUpInside(_ sender: Any) {
// Navigate to child view
self.add(parentViewController: self.mainView, childViewController: self.tab1Controller)
}
}
Hope it helps!
I have a HomeView class with scrollView IBOutlet and a function the changes the offset of the scrollView:
scrollView.setContentOffset(CGPoint(x: self.view.frame.width * 2, y: 0), animated: true)
From the FeedView class I attempt:
let Home = HomeView()
Home.ScrollRight()
But I get this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Because your HomeView created by either XIB or Story board. Thats why below line return nil object.
let Home = HomeView()
If you want to call ScrollRight() method of HomeView from another class then declare global variable of HomeView like
var homeVC = HomeView()
and in viewDidMethod of HomeView
homeVC = self as HomeView
not you can access homeVC from anywhere
and just call method by
homeVC.ScrollRight()
from another viewController.
=======================================
EX
import UIKit
var homeVC = HomeVC()
class HomeVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
homeVC = self as HomeVC
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Now you can use homeVC object and access property of HomeVC anywhere.
NOTE:
If you are calling function from same viewController (HomeView) then you don't need to create object of that viewController. You can just simply call function by below like directly.
ScrollRight() OR self.ScrollRight()
If your scroll view is set up as an outlet it means it is created within your view controller's main view when said view controller is instantiated from a storyboard.
This code:
let Home = HomeView()
...at most instantiates the view controller itself, but it does not load its view (let alone wire the outlets to the appropriate subviews -in this case, your scroll view).
Please read about view controller view life cycle. It's all in Apple's programming guides and countless tutorials online.
I'm learning page controllers and have created a blank app using the Page-Based Application template. By default it creates 12 month pages that you can scroll left to right.
On the storyboard there is a Page View Controller Scene with a Page View Controller. However, in the code, there are commands to create this controller programmatically in the root controller. i.e.
var pageViewController: UIPageViewController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Configure the page view controller and add it as a child view controller.
self.pageViewController = UIPageViewController(transitionStyle: .pageCurl, navigationOrientation: .horizontal, options: nil)
self.pageViewController!.delegate = self
let startingViewController: DataViewController = self.modelController.viewControllerAtIndex(0, storyboard: self.storyboard!)!
let viewControllers = [startingViewController]
self.pageViewController!.setViewControllers(viewControllers, direction: .forward, animated: false, completion: {done in })
self.pageViewController!.dataSource = self.modelController
self.addChildViewController(self.pageViewController!)
self.view.addSubview(self.pageViewController!.view)
// Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
var pageViewRect = self.view.bounds
if UIDevice.current.userInterfaceIdiom == .pad {
pageViewRect = pageViewRect.insetBy(dx: 40.0, dy: 40.0)
}
self.pageViewController!.view.frame = pageViewRect
self.pageViewController!.didMove(toParentViewController: self)
}
In fact, if I delete the Page View Controller from the storyboard, the app works just as before.
I am trying to understand why this would be on the storyboard when it does not appear to be used. Also, are the good reasons to create a Page View Controller programmatically rather than using the storyboard?
Inside storyboard You have a standard UIViewController subclass, not UIPageViewController. Your UIPageViewController is created in code, and then added as a subview to this RootViewController. RootViewController displays UIPageViewController and act as its delegate. I don't think this will work correctly if you delete anything from the storyboard, are you sure about this? There is not instance of UIPageViewController in storyboard.
EDIT
I think my problem is that I add the views as subviews in the same view, thats why I can't remove it ?
Im trying to learn swiping between views using XIB.
My storyboard contains 3 views
-Login
-Create Account
-View with scrollview that scrolls between a tableview and a blank view. This view has an embedded navigation controller (Editor -> Embed In -> Navigation Controller)
I Don't want the navigation controller to be shown in my blank page.
I have created the tableView Controller and the blank UIControllerView by adding them as "addChildViewController", See code below
import UIKit
class MasterViewForScroll: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
let Inbox : FriendlistTableBarView = FriendlistTableBarView(nibName: "FriendlistTableBarView", bundle: nil)
let Camera : CameraViewController = CameraViewController(nibName: "CameraViewController", bundle: nil)
func creatingSubViews() {
self.addChildViewController(Inbox)
self.scrollView.addSubview(Inbox.view)
Inbox.didMoveToParentViewController(self)
Inbox.navigationController?.navigationBar.hidden = false
var CameraView = Camera.view.frame
CameraView.origin.x = self.view.frame.width
Camera.view.frame = CameraView
self.addChildViewController(Camera)
self.scrollView.addSubview(Camera.view)
Camera.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.width * 2, self.view.frame.height)
}
override func viewDidLoad() {
super.viewDidLoad()
creatingSubViews()
}
So my question is: How do I hide the navigation controller in the "Camera" view.
Thank you
In your CameraViewController class, add the following code:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBarHidden = true
}
Next, in your FriendlistTableBarView class (I guess it is a UIViewController subclass), add the following code:
override func viewWillAppear() {
super.viewWillAppear()
self.navigationController?.navigationBarHidden = false
}
So, when you swipe to the right - navigation bar will hide, and when you swipe to the left - it will appear again.