I have a QR code scanner viewcontroller, rather than pushing it in a navigation controller i wondered if it was possible to instantiate the view controller to be an overlay on my previous main screen taking up a quarter of the screen, as i dont need it to be a whole separate screen.
It has its own viewcontroller and view, i just need it to overlay at a smaller size.
No code to provide as this is more of a theoretical question
I assume you want to programmatically add a container in which a second UIViewController can be added in in your current UIViewController. See the example on how to achieve this.
import UIKit
class ParentViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Set a yellow background color on the parentViewController
self.view.backgroundColor = .yellow
// Create our detailViewController which will be contained in the parent
let detailViewController = DetailViewController()
// Add this detailViewController as a child in the current ParentViewController
addChildViewController(detailViewController)
// Add the detailViewController view as a subview on the ParentViewController
view.addSubview(detailViewController.view)
// Since we dont use IB we disable this property to allow programmatic constraints
detailViewController.view.translatesAutoresizingMaskIntoConstraints = false
// We create the constraints for our detailViewControllers view
NSLayoutConstraint.activate([
detailViewController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor),
detailViewController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
detailViewController.view.heightAnchor.constraint(equalToConstant: 200),
detailViewController.view.widthAnchor.constraint(equalToConstant: 200)
])
}
}
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
print("DetailViewController loaded!")
}
}
We have a ParentViewController with a simple yellow background. In this controller we add a green DetailViewController as a container. The result is:
When you run this app you'll notice that the console prints the result from the DetailViewController.
If you want to remove the DetailViewController:
// Call this in your ParentViewController
let vc = self.childViewControllers.last
vc?.view.removeFromSuperview()
vc?.removeFromParentViewController()
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.
I have 10 UIViewControllers or Views in my game. All other view controllers are subclass of the first UIViewController, which in my case is ViewControllerA as shown below. I transition to my Main Menu view or scene using function "home" from every view or scene. I also create and add home button to view programmatically using function "createHomeButton" which is only part of first view controller so that is available in every view controller subclass.
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
// Create and add to the scene home button
func createHomeButton () {
let imageButton = UIImage(named: "home_button")
let button = UIButton(frame: CGRect(x: 680, y: 650, width: 120, height: 100))
button.setImage(imageButton, for: UIControlState.normal)
button.addTarget(self, action: #selector(home(_:)), for: .touchUpInside)
view.addSubview(button)
}
// Dismiss current view controller and transition to Main Menu scene
func home(_ sender: AnyObject) {
NSLog("Transition to Main Menu scene")
let viewController = self.view?.window?.rootViewController
viewController!.dismiss(animated: true, completion: nil)
}
}
Then in my ViewControllerB and the rest of view controllers, which are all subclass of ViewcontrollerA, I don't create home button because I already created it in ViewControllerA which is now available for every subclass view.
class ViewControllerB: ViewControllerA {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
With this setup, when I'm in subclass ViewControllerB and I click on Home Button, it first transitions to ViewControllerA view then to the Main Menu view. Basically from every view controller subclass when that Home Button is pressed, I will transition to ViewControllerA view then to the Main Menu view.
Is there a way to have this setup such that when I'm in either subclass view controller to directly transition to Main Menu scene rather then going to first view then to the Main Menu scene?
I am developing a game where the first View Controller will contain full functionality for the rest of the 30 View Controllers, where the rest of these controllers will be sublass of the first view controller. My plan is to use single storyboard for all 30 view controllers or scenes in my app which will all use the same background image. To give you an idea as to what I'm talking about, I only show 2 scenes in this Drawing.Storyboard image but plan is to have 28 more scenes in this same storyboard.
Drawing.Storyboard
If all 30 scenes in this storyboard will have the same UIView background image, how to handle that. Will I have to add same background image for each view in scene or just add background image to the first scene view and use container view for the rest? Note I have never use container views in the past.
After further research and suggestion by "h44f33z", the following will work without using UIView image in your storyboard.
ViewController A
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// Load background image for all views
let bgImageView = UIImageView(frame: UIScreen.main.bounds)
bgImageView.image = UIImage(named: "bg_image")
self.view.addSubview(bgImageView)
}
}
View Controller B
class ViewControllerB: ViewControllerA {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
There is nothing you have to do in ViewController B because it is a subclass of ViewController A. With this setup you can go with as many as possible views as long as view is subclass of the first view controller.
One way to do that, you can just create a parent / base class of UIViewController and add UIImageView to self.view in viewDidLoad()
So, for the all 30 ViewControllers, should extend to that base viewController. It will look similar to this
class StartViewController: BaseViewController
Your base class viewDidLoad will be something like
override func viewDidLoad() {
super.viewDidLoad()
let bgImageView = UIImageView(frame: UIScreen.mainScreen().bounds)
bgImageView.image = UIImage(named: "bg-image")
self.view.addSubview(bgImageView)
}
You can event add more functions or handling in the base class and will be easily used by all 30 child viewControllers
I have a UIViewController and it contains a button and embedded viewController. By default the viewController is hidden and in the button action I change the hidden parameter of the viewController to false:
override func viewDidLoad() {
super.viewDidLoad()
eventDetail.hidden = true
}
#IBAction func showButtonAction(sender: AnyObject) {
eventDetail.hidden = false
}
And when the hidden element appears - it covers some part of the screen. In my story board it looks like this:
I want to blur the rest, the area of the screen that is not covered by appearing view - is that possible?
On iOS8+, you can use the UIBlurEffect. But personally, I've had a very good experience with this library: https://github.com/nicklockwood/FXBlurView
You need another view, and that view used as background then in your viewDidLoad assign to that view the Blur effect (UIVisualEffectView).
view = UIVisualEffectView()
let lightBlur = UIBlurEffect(style: UIBlurEffectStyle.Light)
view.effect = darkBlur;
After you can use your existing view (the white one) and put in the foreground.
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)
}
}