Xcode 10.1
Swift 4.2
I am using Master-Detail project. I need to add a bottom tab bar within the detail view but I don't want it to be displayed until an object in the Master view is selected.
Right now i used the "Hidden" option under "Drawing" for the tab bar which hides it during the initial launch, but can't find a way to make it displayed after selecting the master object.
class DetailViewController: UIViewController {
#IBOutlet weak var detailHeaderLabel: UINavigationItem!
#IBOutlet weak var detailDescriptionLabel: UILabel!
func configureView() {
// Update the user interface for the detail item.
if let detail = detailItem {
if let label = detailDescriptionLabel {
label.text = detail.description
}
if let headerLabel = detailHeaderLabel {
headerLabel.title = detail.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
configureView()
}
var detailItem: String? {
didSet {
// Update the view.
configureView()
}
}
}
You need to create the IBOutlet of the tabbar from storyboard and than change is hidden property
DispatchQueue.global(qos: .background).async {
// Background Thread
getObjectForMaster()
DispatchQueue.main.async {
tabBar.isHidden = false
}
}
Related
In FourthViewController, I have a slider, which has values ranging from 1 to 1000. The value that is set gets sent via the delegate to PatternViewController, where it should be used to do sth (I put the print for testing purposes).
I've worked with delegates before and it was all ok, checked the code multiple times and multiple answers here on stack, I can't seem to find the issue. Any help would be much appreciated
update: I have added a button so that it would be easier to track along. It turns out that by pressing first time the button, nothing happens. but if I first checkout the PatternViewController, then I go back to FourthViewController and press the button, the delegate gets triggered. anyone got any idea on why is this happening?
FourthViewController
import UIKit
class FourthViewController: UIViewController {
//MARK: Outlets
#IBOutlet var persistenceButton: UIButton!
#IBOutlet var persistenceSlider: UISlider!
#IBOutlet var persistenceLabel: UILabel!
weak var delegate: FourthViewControllerDelegate?
//MARK: Stored Properties - Constants
let userDefaults = UserDefaults.standard
let keyName = "sliderValue"
//MARK: Initializer
override func viewDidLoad() {
super.viewDidLoad()
loadSliderValue()
initialSetUp()
}
//MARK: Actions
#IBAction func handleValueChanged(_ sender: UISlider) {
updateLabel()
persistSliderValue(value: persistenceSlider.value, key: keyName)
}
//MARK: Methods
func updateLabel() {
persistenceLabel.text = String(format: "%.2f", persistenceSlider.value)
}
func persistSliderValue(value: Float, key: String) {
userDefaults.set(value, forKey: key)
}
func loadSliderValue() {
let persistedValue = userDefaults.float(forKey: keyName)
persistenceSlider.value = persistedValue
updateLabel()
}
}
func initialSetUp() {
persistenceButton.addTarget(self, action: #selector(handleButtonPressed), for: .touchUpInside)
}
#objc func handleButtonPressed() {
delegate?.valueChanged(value: persistenceSlider.value)
}
}
PatternViewController
import UIKit
class PatternViewController: UIViewController, FourthViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp() {
if let tabBar = self.tabBarController, let viewController = tabBar.viewControllers, let fourthViewController = viewController[3] as? FourthViewController {
fourthViewController.delegate = self
}
}
func valueChanged(value: Float) {
print(value)
}
}
It depends upon how you instantiated the tab view controller. If you do it with storyboards, for example, the view controllers for the respective tabs are instantiated lazily, only instantiated as the user taps on them. (This helps reduce latency resulting from instantiating all four of the tabs’ view controllers.)
While you theoretically could go ahead and have the tab bar controller instantiate the four view controllers programmatically up front, rather than just-in-time via the storyboard, I might instead consider specifying a UITabBarControllerDelegate for the tab bar controller. Have the tab bar controller’s delegate method update the relevant tab’s view controller’s model.
Here is an example with two tabs, the first has a slider and the second has a label that displays the slider’s value. In this simplified example, I’ve moved the model object (the value associated with the slider) into the tab bar controller, and it passes it to the second view controller when you select the associated tab.
// TabViewController.swift
import UIKit
class TabBarController: UITabBarController {
var value: Float = 0.5
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
// MARK: - UITabBarControllerDelegate
extension TabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let viewController = viewController as? SecondViewController else { return }
viewController.value = value
}
}
And
// FirstViewController.swift
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarController = tabBarController as? TabViewController else { return }
slider.value = tabBarController.value
}
#IBAction func didAdjustSlider(_ sender: UISlider) {
guard let tabBarController = tabBarController as? TabViewController else { return }
tabBarController.value = sender.value
}
}
And
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var value: Float = 0 { didSet { updateLabel() } }
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
updateLabel()
}
func updateLabel() {
label?.text = formatter.string(for: value)
}
}
Probably needless to say, I not only set the base view controller class for the two tab’s view controllers, but also set the base class for the tab bar controller’s storyboard scene to the above TabBarController.
I have a UIViewController (Main Menu) that is displayed after my other UIViewController (Loading Screen) finishes downloading/syncing data from the web. My Main Menu is having problems as it doesn't always display all the UIButtons that are on the view when it shows up! There is an inconsistancy, 1 out of every 10 times the Main Menu loads, all the UIButtons will appear.
Here's how the view should look:
Here's how the view usually shows up:
I can put my finger where the UIButtons should be and move my finger away and they'll appear. I can also tap where they should be and my segues will still trigger, but the UIButtons dont just automatically show up. It looks like
I attempted to add a MainMenuView.setNeedsDisplay() to the ViewDidLoad function and it didn't help. I also created a test UIButton on the view that triggers a MainMenuView.setNeedsDisplay() command but the hidden UIButtons remain hidden.
If I leave the screen idle for 30 or so seconds, all the UIButtons will randomly appear without me having to manually make them appear.
The Main Menu view is normal and all UIButtons are visible if I segue to it from any part of the app aside from my Loading Screen.
Edit: Main Menu Code - (references are Strong)
import UIKit
import CoreData
class MainMenuViewController: UIViewController {
// MARK: References
#IBOutlet var btnGasManifolds: UIButton!
#IBOutlet var btnAirCompressors: UIButton!
#IBOutlet var btnVacuumPumps: UIButton!
#IBOutlet var btnUnitConversion: UIButton!
#IBOutlet var btnCFMCalculator: UIButton!
#IBOutlet var mainMenuView: UIView!
var userData = [NSManagedObject]()
// MARK: Functions
override func viewDidLoad() {
var name = String()
for duserData in userData {
name = duserData.value(forKey: "nameFull") as! String
}
print("Main Menu\n->\(name)")
backgroundSetup()
}
override func viewDidAppear(_ animated: Bool) {
mainMenuView.setNeedsDisplay()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btnTest(_ sender: Any) {
mainMenuView.setNeedsDisplay()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any!) {
if segue.identifier == "settingsSegue" {
let destination = segue.destination as! SettingsViewController
destination.userData = userData
}
}
// Background Setup
func backgroundSetup() {
let background = UIImage(named: "Data Screens")
var imageView : UIImageView!
imageView = UIImageView(frame: view.bounds)
imageView.contentMode = UIViewContentMode.scaleAspectFill
imageView.clipsToBounds = true
imageView.image = background
imageView.center = view.center
view.addSubview(imageView)
self.view.sendSubview(toBack: imageView)
}
}
The code which creates and shows the buttons after fetching the data from server needs to run on Main UI thread. If the code is running the background thread it will not properly display the UI elements and will take some time or manual scroll before showing the controls.
Run the code on Dispatch main queue.
dispatch_async(dispatch_get_main_queue(),{
//do some work
})
OR
Override one of the methods from ViewWillLayoutSubviews and ViewDidLayoutSubviews accordingly and run your code in these methods.
I have a viewController with a UISegmentedControl and a UIButton.
Within this viewController, I have two containers, each containing one viewController with a UITextField inside.
I want to save the values in the textField on the click of the button.
Here's the code I have written so far:
View Controller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//
//
containerA.showView()
containerB.hideView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonTapped(sender: UIButton) {
print(ContainerAViewController.sharedInstance.textFieldA)
}
#IBAction func segmentedControlValueChanged(sender: AnyObject) {
switch(sender.selectedSegmentIndex) {
case 0 : containerA.showView()
containerB.hideView()
case 1 : containerB.showView()
containerA.hideView()
default : containerA.showView()
containerB.hideView()
}
}
#IBOutlet weak var containerA: UIView!
#IBOutlet weak var containerB: UIView!
func hideView(view: UIView) {
view.userInteractionEnabled = false
view.hidden = true
}
func showView(view: UIView) {
view.userInteractionEnabled = true
view.hidden = false
}
}
extension UIView {
func hideView() -> UIView {
self.userInteractionEnabled = false
self.hidden = true
return self
}
func showView() -> UIView {
self.userInteractionEnabled = true
self.hidden = false
return self
}
}
ContainerAViewController:
class ContainerAViewController: UIViewController {
#IBOutlet weak var textFieldA: UITextField!
static let sharedInstance = ContainerAViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
ContainerBViewController:
class ContainerBViewController: UIViewController {
#IBOutlet weak var textFieldB: UITextField!
static let sharedInstance = ContainerBViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
When I tap the button, it gives me the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Can somebody please help?
You should not try to manipulate another view controller's views. That violates the principle of encapsulation, an important principle in object-oriented development.
You should give your child view controllers (ContainerAViewController and ContainerBViewController) string properties, and have the code for those view controllers set that string property when the user enters text into the view controllers' text fields.
Ignoring that design flaw, your code doesn't make sense. You show your CLASS as ContainerAViewController, and yet your buttonTapped method is trying to ask a ContainerAViewController singleton for the value of a text field. That makes no sense.
You want to have properties in your parent view controller that point to your child view controllers.
You should implement a prepareForSegue method in your parent view controller, and in that prepareForSegue method, look for the embed segues that fire when the child view controllers are loaded. When that happens you should set your properties that point to the child view controllers.
I would like to make a UI that have label, table view and one button click. When click on the button, we pop up a half screen view that have lots of buttons. I want user can still click on the rest of the screen also.
So i use the approach that suggest in the post
How To Present Half Screen Modal View?
Method 2: to animate a UIView which is of size half of the existing view.
Then you have to simply follow animation of the UIView.
Here as it is just a UIView that will be added as subview to existing view, you will be able to touch the rest of the screen.
As i am newbie to the ios and swift, I would like to get some suggestions.
Now i am successfully add as subview and show in the half of the screen.
How can i implement to let subview click button result show on parent view label text?
I am thinking about parent.xib and subview.xib have the same UIVeiwController.swift. Then i can #IBOutlet and #IBAction to the same controller swift file and update the result. But don't know it is the accpetable way to do?
If not, how can the subViewController send result/event to the parent view and update in the parent view component?
You could use delegation. This keeps your view controllers decoupled, i.e. prevents the child from having a reference to its parent, which allows other view controllers to interact with the modal view controller in the same way.
class ParentViewController : UIViewController, ModalViewControllerDelegate {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let modalViewContorller = ModalViewController()
modalViewContorller.delegate = self
self.presentViewController( modalViewContorller, animated: true, completion: nil )
}
func modalViewControllerDidProduceResult( modalViewController: ModalViewController, result: String ) {
self.label.text = result
}
}
protocol ModalViewControllerDelegate {
func modalViewControllerDidProduceResult( modalViewController: ModalViewController, result: String )
}
class ModalViewController: UIViewController {
var delegate: ModalViewControllerDelegate?
#IBAction func buttonClicked( sender: AnyObject? ) {
delegate?.modalViewControllerDidProduceResult( self, result: "Hello!" )
}
}
You could also use a closure, which in Swift provides a more concise syntax.
class ParentViewController : UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let modalViewContorller = ModalViewController()
self.presentViewController( modalViewContorller, animated: true, completion: nil )
modalViewContorller.resultBlock = { (result: String) in
self.label.text = result
}
}
}
class ModalViewController: UIViewController {
var resultBlock: ((String) -> ())?
#IBAction func buttonClicked( sender: AnyObject? ) {
self.resultBlock?( "Hello!" )
}
}
I have created common sidemenu for all main view controller using reference https://github.com/evnaz/ENSwiftSideMenu
Now the issue is i have created sidemenu view controller from storyboard instead of using code itself,it will not show anything on side menu.
Ideally it has to show the page which design from story board.
Actually only TableViewController work with this example. i need to work with UIViewController.
Anyone have idea about this ?
Check out the latest version, I added precisely that functionality a few weeks ago: you can now use a UIViewController, no need to use a UITableViewController.
But other than that, I can't tell without more information why it doesn't show up.
I'm using it in several apps and it works ok.
I've got a UINavigationController, which uses a subclass of ENSideMenuNavigationController, and a UIViewController for the menu itself.
This is it, basically:
class MainNavigationController: ENSideMenuNavigationController, ENSideMenuDelegate {
override func viewDidLoad() {
super.viewDidLoad()
var mainMenuViewController: MainMenuViewController = storyboard?.instantiateViewControllerWithIdentifier("MainMenuViewController") as! MainMenuViewController
mainMenuViewController.navController = self
sideMenu = ENSideMenu(sourceView: self.view, menuViewController: mainMenuViewController, menuPosition:.Right)
//sideMenu?.delegate = self //optional
sideMenu?.menuWidth = 240.0 // optional, default is 160
sideMenu?.bouncingEnabled = false
sideMenu?.animationDuration = 0.2
// make navigation bar showing over side menu
view.bringSubviewToFront(navigationBar)
}
// MARK: - ENSideMenu Delegate
func sideMenuWillOpen() {
println("sideMenuWillOpen")
}
func sideMenuWillClose() {
println("sideMenuWillClose")
}
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
super.didRotateFromInterfaceOrientation( fromInterfaceOrientation )
sideMenu?.updateFrame()
}
}
Then I have the menu view itself, also in the Storyboard, which is a UIViewController. Here is a fragment:
class ERAMainMenuViewController: UIViewController {
weak var navController: ERAMainNavigationController?
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var exitButton: UIButton!
#IBOutlet weak var headImage: UIImageView!
let kInset:CGFloat = 64.0
override func viewDidLoad() {
super.viewDidLoad()
// Customize apperance of table view
tableView.contentInset = UIEdgeInsetsMake(kInset, 0, 0, 0) //
tableView.separatorStyle = UITableViewCellSeparatorStyle.SingleLine
tableView.backgroundColor = ERAssistantTheme.sideMenuItemBackgroundColor
tableView.scrollsToTop = false
// Preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = true
// tableView.selectRowAtIndexPath(NSIndexPath(forRow: selectedMenuItem, inSection: 0), animated: false, scrollPosition: .Middle)
}
}