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.
Related
Here is the objects hierarchy in my ViewController in Main.Storyboard
Button same size with superview. I would like to close the keyboard with the button click action that's why I wrote this code scope:
#IBAction func btnCloseKeyboardClick(_ sender: UIButton) {
print("A")
for viewItem in self.view.subviews
{
print("B")
if (viewItem is UITextField)
{
print("C")
let tField = viewItem as! UITextField
tField.resignFirstResponder()
}
}
}
but it does not close the keyboard means I don't see print("C")
I solved this problem like in this link but I would like to ask above situation also
If you only have one textfield in your view and want to dismiss keyboard for that, the simple way is to call view.endEditing(true) in your button action
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)
I followed this tutorial to smoothly hide the statusBar smoothly hide statusBar and everything works fine when I use it on practice projects. I use the code in other project's that do not have SplitVC but have a tabBar and uses a navVC & tableView and everything works fine. In those I can successfully make it appear/disappear.
In my actual project I'm using a SplitViewController for iPad. I noticed when I implemented the directions from the link to my SplitViewController the statusBar wouldn't hide. I then made a new project using Apple's default MasterDetailApp to make sure I wasn't doing anything wrong but it doesn't work there either. I kept all of Apple's original code and only added in the necessary methods to make the statusBar appear/disappear
in info.plist I added the View controller-based status bar appearance and set it to YES
in storyboard I added a purple button to the DetailVC to trigger the statusBar disappearance. I also added in the method to make the backBar button disappear/reappear
I added all the methods to make the statusBar disappear/disappear to the DetailVC scene.
I added a tapGesture to the scene to make the statusBar and backButton reappear
I clicked the Plus button on the Master scene, a date appeared, clicked it to get to the DetailVC, pressed the purple buttonPressed to hide the statusBar and backButton but only the backButton gets hidden. I touch the background and the backButton reappears. The statusBar doesn't move.
I kept all the original code from Apple's project's and added mines below it:
class DetailViewController: UIViewController {
//MARK:- Apple's code
#IBOutlet weak var detailDescriptionLabel: UILabel!
func configureView() {
if let detail = detailItem {
if let label = detailDescriptionLabel {
label.text = detail.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
configureView()
// make backButton and statusBar reappear when scene is tapped
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(showBackButtonAndStatusBar))
view.addGestureRecognizer(tapGesture)
}
var detailItem: NSDate? {
didSet {
configureView()
}
}
//MARK:- Outside of the tapGesture in viewDidLoad everything below here is what I added
// bool to determine wether to hide the statusBar or not
var statusBarShouldBeHidden = false
// api method to allow the staus bar to be hidden
override var prefersStatusBarHidden: Bool{
return statusBarShouldBeHidden
}
// api method to animate status bar appearance/disappearance
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation{
return .slide
}
#IBAction func buttonTapped(_ sender: UIButton) {
// 1. hide backBar button
navigationItem.setHidesBackButton(true, animated: false)
// 2. set bool to true
statusBarShouldBeHidden = true
UIView.animate(withDuration: 0.25){
// 3. api method to allow the statusBar to disappear
self.setNeedsStatusBarAppearanceUpdate()
}
}
//called when background is touched and added to tapGesture in viewDidLoad
#objc func showBackButtonAndStatusBar(){
// 1. set bool to false
statusBarShouldBeHidden = false
UIView.animate(withDuration: 0.25){
// 2. bring statusBar back
self.setNeedsStatusBarAppearanceUpdate()
}
// 3. bring backButton back
navigationItem.setHidesBackButton(false, animated: true)
}
}
How can I get the SplitViewVC to let me hide the statusBar?
It appears that you are trying to hide the status bar through the detail view controller. The status bar in the user interface is controlled only by the split view controller because it is on top of the view controller hierarchy. Therefore, the easiest way to control the behavior of the status bar is to subclass UISplitViewController and then override the prefersStatusBarHidden computed property in the subclass. Also, make sure you go to your storyboard and change the split view controller's custom class field in the Identity inspector to your subclass.
---Updated Answer---
#LanceSamaria Okay, I took your code above and tweaked some things. First of all, I only added the button action and not the tap gesture. Also, I commented out the hiding the back button, because this is important in the UI in order to be able to go back to the master view. Anyway, now when you click the button, the SplitViewController will hide the status bar. If you click the button again, then the status bar will reappear.
import UIKit
class DetailViewController: UIViewController {
#IBOutlet weak var detailDescriptionLabel: UILabel!
var statusBarShouldBeHidden = false
func configureView() {
// Update the user interface for the detail item.
if let detail = self.detailItem {
if let label = self.detailDescriptionLabel {
label.text = detail.description
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
/* override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation{
return .slide
} */
var detailItem: NSDate? {
didSet {
// Update the view.
self.configureView()
}
}
#IBAction func buttonTapped(_ sender: UIButton) {
// 1. hide backBar button
//navigationItem.setHidesBackButton(true, animated: false)
// 2. set bool to true
statusBarShouldBeHidden = !statusBarShouldBeHidden
UIView.animate(withDuration: 0.25){
// 3. api method to allow the statusBar to disappear
guard let svc = self.splitViewController as? SplitViewController else { return }
svc.statusBarShouldBeHidden = self.statusBarShouldBeHidden
svc.setNeedsStatusBarAppearanceUpdate()
}
}
}
Also, one more really important thing. Below is my code for the split view controller subclass. Note that I use the same variable name "statusBarShouldBeHidden" in both the split view controller and the detail controller.
import UIKit
class SplitViewController: UISplitViewController {
var statusBarShouldBeHidden = false
override func viewDidLoad() {
super.viewDidLoad()
}
override var prefersStatusBarHidden: Bool {
return statusBarShouldBeHidden
}
}
Thank you for posting this question. This has helped my learn a lot trying to solve this problem. Please let me know if you still have a question about what this.
I have an unwind segue which takes a few seconds to complete while it saves images to disk. I want to display an activity indicator until the view is dismissed but the view doesn't update.
This is my function, which is called from the view controller before it's dismissed:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "saveRecord" {
print("indicator")
let indicator = UIActivityIndicatorView()
indicator.frame = self.view.frame
indicator.activityIndicatorViewStyle = .whiteLarge
indicator.color = UIColor.blue
indicator.hidesWhenStopped = true
indicator.startAnimating()
self.view.addSubview(indicator)
self.view.layoutSubviews()
print("laid out subviews")
}
}
The two print statements execute, and the debugger shows that the indicator has been added to the main view as a subview, but it doesn't appear on screen. Am I missing something?
I know the position of the indicator isn't a problem because running the same code in viewDidLoad correctly shows it in the middle of the screen.
UPDATE
I've recreated the segue functions using a delegate and it's saving everything correctly, but the problem remains. Still no activity indicator.
#IBAction func saveRecord(_ sender: Any) {
print("indicator")
let indicator = UIActivityIndicatorView()
indicator.frame = self.view.frame
indicator.activityIndicatorViewStyle = .whiteLarge
indicator.color = UIColor.blue
indicator.hidesWhenStopped = true
indicator.startAnimating()
self.view.addSubview(indicator)
self.view.layoutSubviews()
print("laid out subviews")
saveImages()
print("saved images")
self.delegate?.saveRecord(name: name!, category: category)
print("saved record")
self.navigationController?.popViewController(animated: true)
}
UPDATE 2
I'm really confused now! This starts the indicator:
#IBAction func saveRecord(_ sender: Any) {
print("indicator")
indicator.startAnimating()
//saveImages()
//print("images saved")
//performSegue(withIdentifier: "saveRecord", sender: self)
}
but this doesn't:
#IBAction func saveRecord(_ sender: Any) {
print("indicator")
indicator.startAnimating()
saveImages()
print("images saved")
performSegue(withIdentifier: "saveRecord", sender: self)
}
The problem is that the UI doesn't update until the saveRecord function has completed, but then the segue is called so the view controller is immediately dismissed. I solved it using dispatch queues - another new skill I've learned today!:
#IBAction func saveRecord(_ sender: Any) {
indicator.startAnimating()
DispatchQueue.global(qos: .userInitiated).async {
self.saveImages()
DispatchQueue.main.async {
self.performSegue(withIdentifier: "saveRecord", sender: self)
}
}
}
I think you should use popViewController instead of an unwind segue. Then you have more control with a custom function on the back button.
What you can do then is:
when you click on the back button (or save image button), add you indicator
then save the image
when the image is saved remove the indicator
pop the view controller
You can't update your views in prepare(for:); the segue is already underway.
Rather than triggering the unwind segue directly from the UI element, trigger an action method that shows the activity indicator and performs the save and then calls performSegue(withIdentifier:"yourUnwindSegueIdentifier", sender:self) to actually perform the unwind.
I'm trying to add TopView in my application, it will be the same for each views. I do it like this
let vcTopMenu = storyboard?.instantiateViewControllerWithIdentifier("TopMenu")
let win:UIWindow = UIApplication.sharedApplication().delegate!.window!!
win.rootViewController = vcTopMenu
win.makeKeyAndVisible()
But when I add other viewControllers (I do it transparent) I can see buttons of TopView, but I can't click on it. It's a code from TopView
override func viewDidLoad()
{
super.viewDidLoad()
print("loaded")
}
#IBAction func btn(sender: AnyObject)
{
print("do something")
}
I see "loaded", but clicking doesn't work, how can I click through view? Thanks!
If I understand your question correctly, you're placing a translucent/transparent UIView on top of another UIView with a button you want to press?
The topmost UIView by default receives the touches. More on this here.
It's not a very standard/practical way to do things, but if you absolutely must, check out this answer: https://stackoverflow.com/a/4010809/4396258