In this app, I'm looking to allow the user to change the background colour of the main menu.
within the MenuVC, the user will click on a button labelled, "Choose a colour". A 'ColorPickerVC' will present from a XIB file, modally.
I created an #IBAction for this and contains the following code:
#IBAction func colorPickerPressed = (_ sender: Any) {
let colorPicker = ColorPickerVC()
colorPicker.modalPresentationStyle = .custom
present(colorPicker, animated: false, completion: nil)
}
The colour picker loads modally from a XIB file and displays 9 colours (UIButton) in a grid format.
When the users taps a colour, the background of the ColorPickerVC changes to the that colour:
#IBAction func tile1(_ sender: Any) {
view.backgroundColor = tile1Color.backgroundColor
The user then taps a 'Confirm' button, this dismisses the view and returns to the MenuVC:
#IBAction func confirmBtnPressed(_ sender: Any) {
dismiss(animated: false, completion: nil)
}
Here's what i'm struggling with:
During the 'ConfirmBtnPressed' action, I would like the MenuVC background colour to also change to what the ColorPickerVC background is (The colour the user selected earlier)
Any ideas on how to achieve this?
Thanks,
Scott
You should learn about the delegate pattern:
First create a protocol that defines the desired function:
protocol ColorPickerProtocol: class {
func didSelectColor(_ color: UIColor)
}
Extend your ViewController with that protocol and implement the method:
class BackgroundColorViewController: UIViewController, ColorPickerProtocol {
func didSelectColor(_ color: UIColor) {
view.backgroundColor = color
}
}
Inside your Xib create a way to pass the delegate and call the didSelectColor function on the desired buttonPress:
class ColorPickerXib {
weak var delegate: ColorPickerProtocol?
#IBAction func dismissButtonPressed(_ sender: UIButton) {
delegate?.didSelectColor(color)
self.dismiss()
}
}
And finally instantiate your Xib in your ViewController with the proper delegate and show it:
let colorPicker = ColorPickerXib()
colorPicker.delegate = self
// Show Xib
The simplest way…
When the user picks a color save that color to the user defaults (so it will persist rather than being a one off color).
In the menu VC's viewWillAppear read the color out of the user defaults.
Typically you would use a delegate protocol for this kind of thing, but given that it is just a color and that color likely needs to be remembered user defaults is the easier route (as you will have to save the color there at some point and read it back out as well)
Related
I am trying to have a switch appear in the off state once a view loads. Not always, but only if a boolean value that I created("switchBool") is false.
I've tried using the two ways on the apple documentation website. The two ways are shown in my code example. One is commented out.
import UIKit
class ViewController: UIViewController {
var switchBool = false
#IBAction func switchControl(_ sender: UISwitch) {
//sender.isOn = switchBool
sender.setOn(switchBool, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
The app is building and running without errors. However, I want the switch to be in the off state if "false" is assigned to the bool "switchBool", which in my example it is, but no matter what I try the switch always appears in the on state when the view loads up.
You need to create an IBOutlet for your switch(using the assistant editor button and storyboard). Then, you can just set the switch to be off in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
yourSwitch.setOn(switchBool, animated: true)
}
In the storyboard you can set it to be off in the Attribute inspector
I have an application with a main UIViewController. When I press a button ("Save") I have a custom popup (UIViewController) present itself. Within this popup I have another button, whereby if I press it I want to dismiss the current popup view controller and then immediately present another different custom popup view controller. I am able to dismiss the first popup , but then i get an error(see below). I am using a protocol to get this working, but I am making a mistake somewhere. Please can some one advise?
[![enter image description here][1]][1]
class mainViewController: UIViewController, popUpDismissedDelegate{
// CUSTOM PROTOCOL DELEGATE FUNCTION
func popUpDimissed() {
// SHOW ANOTHER POPUP TO CREATE CUSTOM HASHTAGS!
let createTagVC = storyboard?.instantiateViewController(withIdentifier: "createTag") as! CreateHashTagPopUpViewController
present(createTagVC, animated: true, completion: nil)
}
// SAVE PDF
#IBAction func savePdf(_ sender: Any) {
// SHOW CUSTOM SELECTION OH HASHTAGS TO ASSIGN PDF
let popUpVC = storyboard?.instantiateViewController(withIdentifier: "hashtagpicker") as! CustomHashTagPopup
popUpVC.delegate = self
present(popUpVC, animated: true, completion: nil)
}
}
protocol popUpDismissedDelegate {
func popUpDimissed()
}
class CustomHashTagPopup: UIViewController, UITableViewDelegate, UITableViewDataSource{
var delegate: popUpDismissedDelegate!
// TAP ON CELLS
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if(indexPath == [0,0]){
// OPTION TO CREATE A NEW HASHTAG
self.dismiss(animated: true) {
self.delegate.popUpDimissed()
}
}else{
// DO NOTHING
// TO SELECT A HASH TAG USE NEEDS TO PRESS ON THE CHECKBOX!
}
}
}
ERROR:
Warning once only: Detected a case where constraints ambiguously
suggest a height of zero for a tableview cell's content view. We're
considering the collapse unintentional and using standard height
instead.
popup - viewDidDisappear
Could not cast value of type 'UIViewController' (0x10c1ec1f0) to
'zapdocuments.CreateHashTagPopUpViewController' (0x107cd0520).
2018-08-28 18:18:48.196815+0100 zapdocuments[28989:4660894] Could not
cast value of type 'UIViewController' (0x10c1ec1f0) to
'zapdocuments.CreateHashTagPopUpViewController' (0x107cd0520).
You didn't actually post the error, but I think I know what the problem is. You're probably trying to present the next VC before the old one actually dismissed.
You should use the completion parameter of the dismiss method and put your delegate callback in there, to ensure you don't try to do anything until it's fully dismissed.
Move your presentation of createTagVC to the next loop of the runloop. This should guarantee that the UI is in the correct "non-presenting" state.
DispatchQueue.main.async {
present(createTagVC, animated: true, completion: nil)
}
Sometimes, some things aren't really done until the runloop ends. Best to start fresh in the next one.
I have three buttons that change the colour of my background. I want to hide the RED button when it's pushed. The show it if GREEN or BLUE button is pushed.
The same goes for the GREEN and BLUE buttons.
I can't find a way to call removeFromSuperview. In ObjectiveC I used to do mybutton.hidden = true
but this doesn't work.
ViewController: UIViewController {
#IBAction func RED(_ sender: Any) {
print("background was \(String(describing: self.view.backgroundColor))")
self.view.backgroundColor = UIColor.red
print("background is now \(String(describing: self.view.backgroundColor))")
}
#IBAction func GREEN(_ sender: Any) {
print("background was \(String(describing: self.view.backgroundColor))")
self.view.backgroundColor = UIColor.green
print("background is now \(String(describing: self.view.backgroundColor))")
}
#IBAction func BLUE(_ sender: UIButton) {
print("background was \(String(describing: self.view.backgroundColor))")
self.view.backgroundColor = UIColor.blue
print("background is now \(String(describing: self.view.backgroundColor))")
}
You need to create an IBOutlet for each button in order to access the "isHidden" property. Right now you only have an IBAction defined for each (at least in the code you've provided). To create the IBOutlet, control-drag from the button to your view controller's code, similar to what you did to create the IBActions you already have. In the popup, make sure that "Connection" says "Outlet" and "Type" is "UIButton". Name them as desired (e.g., redButton, blueButton). Then you can type "redButton.isHidden = true" in the appropriate location.
An IBAction only allows you to control what the button will do when it is pressed. An IBOutlet is required to access the properties of a UIButton.
My Situation:
There are a View_A (UICollectionViewController&UICollectionViewCell) and View_B (UIViewController). I want to switch to the View_B when I touched one of cell in the View_A with Segue in the StoryBoard.
In the StoryBoard, I connected View_A and View_B with the Push Segue which identifier is SegueToView_B.
And the function of switchViews just worked fine.
My Problem:
With the Push Segue, I do not need to add a BackButton (NavigationItem) to turn back to the View_A, because there is a 'NavigationItem' be crated automatically by system. And I tried other type segues, like Modal, Popover, and the NavigationItem was not created automatically. I want to ask why?
I want to set the specific color, not the default blue, for that NavigationItem which be created by system automatically, but I failed to find it. After that I just set the color in the prepareForSegue(), but it did not work. Please tell how to set the specific color for it?
My Code:
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.collectionView?.setPresenting(true, animated: true, completion: nil)
let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
self.selectedCard = delegate.otherCards[indexPath.row]
self.performSegueWithIdentifier("SegueToView_B", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if identifier == "SegueToView_B" {
let myOtherCardViewController = segue.destinationViewController as? View_BViewController
myOtherCardViewController!.otherCard = self.selectedCard
myOtherCardViewController!.navigationItem.backBarButtonItem?.tintColor = UIColor.whiteColor() // Failed to work!!!
myOtherCardViewController?.navigationItem.leftBarButtonItem?.tintColor = UIColor.whiteColor() // Failed to work, too!!!
}
}
}
Thanks for your help.
Ethan Joe
To set the tintColor for that navigation bar:
myOtherCardViewController.navigationBar.tintColor = .whiteColor()
Why there is no Navigationbar when you use the Modal or PopOver? Because thats how Modal and Popover work! You have to create another Navigation controller for the view you are connecting with the Modal segue, like this:
Another technique I am using is, to create a single NavigationController class, and set all the desired properties (color, font etc.) and then link all the NavigationControllers in the Storyboard to that NavigationController class.
With that you wont have to reconfigure every NavigationController.
Your Solution
You can set back button hidden in View_B controller in viewDidLoad method like this.
class View_BViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.hidesBackButton = true;
// Do any additional setup after loading the view.
}
}
To set tint color, you have to create subclass of UINavigationController, and assign that class to your UINavigationController in UIStoryboard
You subclass will look like this, to set tint color,
class navigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
//self.navigationBar.barStyle = UIBarStyle.Default
self.navigationBar.tintColor = UIColor.redColor()
// Do any additional setup after loading the view.
}
//Other stuff
}
May this help you!!
I am re-writing a tutorial converting the code from Objective-C to swift. The app moves from VC one where there is 3 sliders (Red, Green and Blue) that set the background colour, a label of the colour name and a button that links to the second VC. In the second VC the colour from the first VC is used as the background and the user has a chance to name the colour.
When the user enters the colour name it should return the new colour name to the orginal VC and the label that shows the colour name should show the text entered.
The following is the code that is causing issue:
func textFieldShouldReturn(nameEntry: UITextField) -> Bool
{
ViewController().colourLabel.text = nameEntry.text
nameEntry.resignFirstResponder()
dismissViewControllerAnimated(true, completion: nil)
return true
}
The error "fatal error: unexpectedly found nil while unwrapping an Optional value" is generated. However debugging nameEntry.text has a string in it.
I'm a little stumped. I could try and do a prepare for unwind segue but it is meant to be a tutorial app.
Cheers
ViewController() actually creates a new instance of your ViewController. This is not a reference to the already existing ViewController. What you can do is create a weak variable pointing to first ViewController inside the second ViewController and set it at prepareForSegue or when the second View controller is shown.
class SecondViewController : UIViewController {
weak var firstViewController : ViewController?
// Other code
func textFieldShouldReturn(nameEntry: UITextField) -> Bool
{
firstViewController?.colourLabel.text = nameEntry.text
nameEntry.resignFirstResponder()
dismissViewControllerAnimated(true, completion: nil)
return true
}
}
Inside First View Controller prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SecondViewController" {
let secondViewController = segue.destinationViewController as SecondViewController
secondViewController.firstViewController = self
}
}
It's possible that the view controller returned by ViewController() has not yet loaded its views. You could try checking this in a setter function and storing it for later use once the views have been loaded.
class VC : UIViewController {
#IBOutlet weak var colourLabel: UILabel!
var savedLabelText: String?
override func viewDidLoad() {
super.viewDidLoad()
self.colourLabel.text = self.savedLabelText
}
func setColorLabelText(label: String) {
if self.isViewLoaded() {
self.colourLabel.text = label
}
else {
self.savedLabelText = label
}
}
}