modifiy and customize the behavior of the UINavigationBar back button - ios

I have a ViewController embeded in a navigationBar. I want the back button in the navigation bar to have the following effect :
if a condition is fullfilled, apply the normal behavior of the back button
if the condition is not fullfilled, stay and remain on the same VC to do something else

Try like this :-
override func viewDidLoad() {
let transparentButton = UIButton()
transparentButton.frame = CGRectMake(0, 0, 50, 40)
transparentButton.backgroundColor = UIColor.orangeColor()
transparentButton.addTarget(self, action:"backAction:", forControlEvents:.TouchUpInside)
self.navigationController?.navigationBar.addSubview(transparentButton)
}
And the function is
func backAction(sender:UIButton) {
// check your condition
}

Related

why does a custom button which added by code in UITabBarController.viewDidLoad doesn't response the selector

I add a custom button to the tabBar in my MyViewController.viewDidLoad(subclass of UITabBarController)
But I find it doesn't response the selector.
If I delay one second to add button(in DispatchQueue.main.asyncAfter closure) ,it works OK.
I think it's not the right way to resolve it.
func addButton() {
let button = UIButton(type: UIButton.ButtonType.custom)
button.bounds = CGRect(x:0,y:0,width:30,height:30);
button.backgroundColor = UIColor.red
button.center = CGPoint(x:self.tabBar.frame.size.width/2, y:self.tabBar.frame.size.height/2 - 20);
button.addTarget(self, action: #selector(click(button:)), for: UIControl.Event.touchUpInside)
tabBar.addSubview(button)
}
You have added button to UITabBar of UITabBarController as half of part of the button would appear above the Tabbar and half of below the Tabbar as per frame.
So I guess you will not get click on part of that button which is out of Tabbar(above Tabbar) would not get touch. I you will make button little big OR try to click with arrow in simulator, you will get idea.
If you need to have button at bottom but slightly upper, then please create custom Tabbar to achieve design like this. Or else you can add that button into UITabBarController’s view instead of Tabbar.
class MyTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.addButton()
}
func addButton() {
let button = UIButton(type: UIButton.ButtonType.custom)
button.bounds = CGRect(x:0,y:0,width:50,height:50); //1
button.backgroundColor = UIColor.purple
button.center = CGPoint(x:self.tabBar.frame.size.width/2, y:self.tabBar.frame.size.height/2 - 50 + self.tabBar.frame.origin.y); //2
button.addTarget(self, action: #selector(click(button:)), for: UIControl.Event.touchUpInside)
button.layer.cornerRadius = button.frame.size.height/2
button.layer.masksToBounds = false
button.layer.shadowColor = UIColor.black.withAlphaComponent(0.5).cgColor
button.layer.shadowRadius = 5.0
button.layer.shadowOffset = CGSize(width: 0.0, height: 5.0)
button.layer.shadowOpacity = 0.5
//tabBar.addSubview(button) //3
self.view.addSubview(button). //4
}
#objc func click(button: UIButton) {
print("Button get clicked")
}
}
I have marked four things with commented by numbers at the end of lines, that you can make to your code and try.

TableView HeaderSections hide buttons

I have been trying to fix an issue that I encountered with a tableviewcontroller.
The sections within the tableviewcontroller are views:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionLabel = UILabel()
sectionLabel.text = Catalog.sharedInstance.allSections[section - 1].name.localized(lang: defaults.string(forKey: "language")!)
sectionLabel.font = UIFont(name: "Centabel Book", size: 25)
sectionLabel.backgroundColor = UIColor.white
sectionLabel.clipsToBounds = true
return sectionLabel
}
If I try to add a button programatically to put it over the tableview
let actionButton = JJFloatingActionButton()
override func viewDidLoad() {
super.viewDidLoad()
// Configuration of the Floating Action Button
actionButton.buttonColor = .red
actionButton.addItem { item in
self.performSegue(withIdentifier: "goToSettings", sender: nil)
}
actionButton.display(inViewController: self)
actionButton.clipsToBounds = true
// This doesn't work. It is to bring the button to the front. Now it is hidden by the sections.
view.bringSubviewToFront(actionButton)
// Checks if the language setting is nil, which means it is the first time you run the application. If then... selects English as default.
if defaults.string(forKey: "language") == nil {
defaults.set("en", forKey: "language")
}
}
... I don't know why but the viewForHeaderInSection hides the button. You can check it in the picture below:
floating button hided by the headersection
I tried to use the method:
view.bringSubviewToFront(actionButton)
and also:
actionbutton.superview?.bringSubviewToFront(actionButton)
But none of this brings the button to the front.
I am using a floating action button from github called JJFloatingActionButton. But I tried to add a simple UIButton and I got the same error. This is the code that also gave me the same error inside viewDidLoad:
let button = UIButton(frame: CGRect(x: 100, y: 1000, width: 100, height: 50))
button.backgroundColor = .green
button.setTitle("Test Button", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(button)
Again the same error. You can check the picture:
adding a simple UIButton happens the same problem
May be you can help me with that.
Thanks in advance.
You could try to use a regular UIViewController (not UITableViewController) and add the UITableView by hand.
Like that you would have better control over the view hierarchy.

make pop up view disappear

I have button, when I click that button, a view pops up.
testingButton.addTarget(self, action: #selector(popupTable), for: .touchUpInside)
#objc func popupTable(){
self.opaqueView = self.setUpPopView()
mainView.addSubview(opaqueView)
print("button clicked")
}
if the view is present, I want to click on the button again and make the view disappear.
if tinyView.isHidden == false {
tinyView.isHidden = false
self.testingButton.addTarget(self, action: #selector(removePopUp), for: .touchUpInside)
}else{
tinyView.isHidden = true
mainView.addSubview(tinyView)
}
Which it does but when I click on the button a third time (pop up view is not present) i want the pop up view to appear again but it doesn't.
show pop up
func setUpPopView () -> UIView {
tinyView.backgroundColor = UIColor(red:0.99, green:0.99, blue:0.99, alpha:1.0)
tinyView.layer.cornerRadius = 3
tinyView.layer.masksToBounds = false
tinyView.layer.shadowColor = UIColor.black.withAlphaComponent(0.4).cgColor
tinyView.layer.shadowOffset = CGSize(width: 0, height: 0)
tinyView.layer.shadowOpacity = 0.9
let post: UIButton = UIButton(frame: CGRect(x: 5, y: 10, width: 150, height: 50))
post.backgroundColor = UIColor.clear
post.setTitleColor(UIColor.black, for:UIControlState.normal)
post.setTitle("All Post", for: .normal)
post.contentHorizontalAlignment = .left
post.addTarget(self, action: #selector(removePopUp), for: .touchUpInside)
tinyView.addSubview(post)
}
remove pop up
#objc func removePopUp(_ sender: AnyObject){
self.tinyView.removeFromSuperview()
}
You should use one action only and check the visibility of the pop up view in the action. According to the visibility of the view you can then show or hide the pop up.
Like this:
#objc func popupAction() {
If self.popUpView.isHidden {
// Pop up view is hidden, so show the pop up view again
} else {
// Pop up view is visible, so hide the pop up view
}
}
Also change this:
if tinyView.isHidden == false
{
tinyView.isHidden = false
self.testingButton.addTarget(self, action: #selector(removePopUp), for: .touchUpInside)
}else{
tinyView.isHidden = true
mainView.addSubview(tinyView)
}
With this:
if tinyView.isHidden == false
{
tinyView.isHidden = true
tinyView.removeFromSuperview()
}else{
tinyView.isHidden = false
mainView.addSubview(tinyView)
}
You should not be calling removeFromSuperview. You should not be saying if. It all just adds unnecessary complications.
If your goal is to show and hide a view, just show it and hide it: toggle its hiddenness, which is a one-liner:
tinyView.isHidden = !tinyView.isHidden
That way, if it is invisible, it becomes visible, and if it is visible, it becomes invisible. In Swift 4.2, it's even simpler:
tinyView.isHidden.toggle()
That's all! You don't need know whether it's already hidden, you don't need to remove it from the superview. No if, no nothing. You just invert its hiddenness each time you want to hide or show it.

Set picker values by tapping anywhere outside of a popup view in iOS

Currently have a popup view which sets 3 picker values by tapping on the SET button:
However, I want to remove the SET button altogether, and have the picker values set upon tapping outside of the popup, which in turn hides the popup.
Here is the current code:
// function for selecting picker values
func pickerDidSet() {
let focusPeriodChoice = focusPeriodDataSource[pickerView.selectedRow(inComponent: 0)]
let breakPeriodChoice = breakPeriodDataSource[pickerView.selectedRow(inComponent: 1)]
let repeatCountChoice = repeatCountDataSource[pickerView.selectedRow(inComponent: 2)]
persistPickerChoice(focusPeriodChoice, dataType: .focusPeriod)
persistPickerChoice(breakPeriodChoice, dataType: .breakPeriod)
persistPickerChoice(repeatCountChoice, dataType: .repeatCount)
timerSummaryLabel.text = "\(focusPeriodChoice)m • \(breakPeriodChoice)m • \(repeatCountChoice)x"
UIView.animate(withDuration: 0.2, animations: { self.pickerContainerView.alpha = 0.0 }, completion: { finished in
self.pickerContainerView.isHidden = true
})
}
// Open popup, by tapping gear icon
#IBAction func openSettings(_ sender: Any) {
pickerView.selectRow(pickerChoiceIndex(forDataType: .focusPeriod), inComponent: 0, animated: false)
pickerView.selectRow(pickerChoiceIndex(forDataType: .breakPeriod), inComponent: 1, animated: false)
pickerView.selectRow(pickerChoiceIndex(forDataType: .repeatCount), inComponent: 2, animated: false)
self.pickerContainerView.isHidden = false
UIView.animate(withDuration: 0.2) {
self.pickerContainerView.alpha = 1.0
}
}
// Once pickers have been set, display the summary
private func configureSummaryLabel() {
let focusPeriodChoice = pickerChoice(forDataType: .focusPeriod)
let breakPeriodChoice = pickerChoice(forDataType: .breakPeriod)
let repeatCountChoice = pickerChoice(forDataType: .repeatCount)
timerSummaryLabel.text = "\(focusPeriodChoice)m • \(breakPeriodChoice)m • \(repeatCountChoice)x"
}
// Setting the picker “SET” button
private func addPickerSetButton(atX x: CGFloat, centerY: CGFloat) {
pickerSetButton.frame = CGRect(x: x, y: 0, width: 40, height: 20)
pickerSetButton.center = CGPoint(x: pickerSetButton.center.x, y: centerY)
pickerSetButton.setTitle("SET", for: .normal)
pickerSetButton.setTitleColor(UIColor.white, for: .normal)
pickerSetButton.setTitleColor(UIColor.darkGray, for: .highlighted)
pickerSetButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 17)
pickerSetButton.addTarget(self, action: #selector(pickerDidSet), for: .touchUpInside)
pickerHeaderView.addSubview(pickerSetButton)
}
If the Previous Black View is you default view of ViewController then all you need is to implemented below method.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Check that the touched view is your background view
if touches.first?.view == self.view {
// Do What Every You want to do
}
}
Detail
Every ViewController has a default view object. As in your case the black overlay displaying behind your popup seems like the default view of that view controller. If that black overlay is not your default view then create and IBOutlet of that view which is black opacified in color. And then in the above method where you are check that which view is touch check that if touched view is your black view or not.
Suppose you black view's IBOutlet is backgroundView then the above check will be something like this.
if touches.first?.view == self.backgroundView {
//It means you have touched outside the pop and out side the pop there is only your backgroundView.
//Here you should do exactly the same which you were doing when `SET` button was clicked.
}
touchesBegan method didn't work if touched object is a button so as per you logic.
You need to check if the PickerView is visible then disable it instead of firing the other feature of that button.
Example.
Create a boolean variable named isPickerViewVisible in your class and when picker view is going to visible make it true and when picker view is getting hide just make it false. There might be an IBAction for that red button.
#IBAction didTapButton(_ sender: Any){
//Here you need to check if pickerView is open then disable it. I don't know what logic you have implemented to show picker view.
if isPickerViewVisible {
self.pickerDidSet()
}else {
//Here you should do the task that you do on clicking this button.
}
}

Remove SubView Faster

In my main UIViewController embed in UINavigationController I have add an UILabel to a navigationBar using that code:
if let navigationBar = self.navigationController?.navigationBar {
let frameDomanda = CGRect(x: navigationBar.frame.width/2 - domandaN.frame.width/2, y: -10, width: domandaN.frame.width, height: navigationBar.frame.height)
domandaN.frame = frameDomanda
let secondLabel = UILabel(frame: secondFrame)
secondLabel.text = "Second"
navigationBar.addSubview(domandaN)
}
But when I change Controller the UILabel is fixed. It doesn't disappear so I've added that code:
override func viewDidDisappear(animated: Bool) {
domandaN.removeFromSuperview()
}
It works but I want it to disappear immediately after the press of the back button. Not like this image:
(The "example" text goes away later)
Just add it in viewWillDisappear instead this:
override func viewWillDisappear(animated: Bool) {
domandaN.removeFromSuperview()
}
For the animation parameter:
If true, the disappearance of the view is being animated.
You can use viewWillDisappear, and don't forget to call super:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// you code here
}

Resources