UITabBar delegate method not being called (Programatically created UI) - ios

I'm somewhat new at developing iOS applications, I'm creating an app but I'm not using any storyboards.
I want to use a UITabBar, but when I click (or tap) any item of the bar, the delegate didSelect method is not being called.
I don't know what I am doing wrong, this is my code:
class MainMenuVC: BaseVC, UITabBarDelegate {
var tabBarMenu : UITabBar?
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarMenu = UITabBar.init()
self.tabBarMenu?.delegate = self;
tabBarMenu!.items = barItems // I omitted the code to populate this array on purpose
// Tab Style
tabBarMenu!.backgroundColor = Constants.Colors.gray
tabBarMenu!.tintColor = Constants.Colors.gray
tabBarMenu!.barTintColor = Constants.Colors.gray
tabBarMenu!.isTranslucent = false
tabBarMenu!.isUserInteractionEnabled = true
//Tab bar position
tabBarMenu!.frame = CGRect(x: contentView.frame.origin.x, y: contentView.frame.size.height - Constants.Dimensions.tabBarHeight, width: contentView.frame.size.width, height: Constants.Dimensions.tabBarHeight)
// Adding
self.view.addSubview(tabBarMenu!)
self.view.bringSubviewToFront(tabBarMenu!)
}
// This is not being called
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
self.handleTabBarTab(tag: item.tag)
}
}
My BaseVC is a regular UIViewController, I'm not using UITabBarViewController

Make sure to have an TAG for UITabBarItem, this works fine for me:
self.tabBarMenu = UITabBar(frame: CGRect(x: contentView.frame.origin.x, y: contentView.frame.size.height - Constants.Dimensions.tabBarHeight, width: contentView.frame.size.width, height: Constants.Dimensions.tabBarHeight))
self.tabBarMenu?.delegate = self
let tabOneBarItem = UITabBarItem(title: "Test", image: nil, tag: 1)
let tabTwoBarItem = UITabBarItem(title: "Test2", image: nil, tag: 2)
tabBarMenu!.items = [tabOneBarItem, tabTwoBarItem]

The code I posted here is a bit different from the code I'm actually working on.
What was actually causing trouble were some super UIViews where I was adding all the other views, including the UITabBar. These views were not enabled to user interaction.
So all I did was:
// Enabled the main view container to user interaction
// This was actually causing the issue
self.containerView.isUserInteractionEnabled = true
// Adding the views
self.containerView.addSubview(tabBarMenu!)
self.containerView.bringSubviewToFront(tabBarMenu!)
Everything worked good after that.
So this approach is actually functional

Related

Making NavigationBar subview clickable in Swift

I have a View Controller embedded in Navigation Controller. The view has 1 WKWebView, hence, I'm setting view = webView in loadView() override.
So, I'm adding a small little sub navigation bar underneath my navigation controller to allow a user to change their location.I can add the subview to the navigation controller, I'm just not able to make it clickable.
override func loadView() {
let config = WKWebViewConfiguration()
config.processPool = YourModelObject.sharedInstance.processPool
webView = WKWebView(frame: .zero, configuration: config)
webView.navigationDelegate = self
self.webView.scrollView.delegate = self
view = webView
..
if let navigationBar = self.navigationController?.navigationBar {
let secondFrame = CGRect(x: 50, y: 44.1, width: navigationBar.frame.width, height: 30)
let secondLabel = UILabel(frame: secondFrame)
secondLabel.textColor = .black
secondLabel.text = "Getting your location..."
secondLabel.isUserInteractionEnabled = true
let guestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(setLocation(_:)))
secondLabel.addGestureRecognizer(guestureRecognizer)
secondLabel.textAlignment = .left
secondLabel.font = secondLabel.font.withSize(14)
secondLabel.tag = 1002
navigationBar.addSubview(secondLabel)
}
}
And then the setLocation function
#objc func setLocation(_ sender: Any) {
print("location label tapped")
}
But when I tap the label, I'm not getting anything printed in console. I don't know if the use of target: self is wrong for the tapGestureRecognizer or what's going on here.
I too am new to Swift, so my answer is far from guaranteed. I just know what it's like to be in your position,
Perhaps try creating a subclass of navigationBar for the sub navigation bar, i.e. mySubNavigationBar. Then in the subclass's code do all the initialization that you need to do. Including the print line so you'll know if you're getting there.
p.s. I would have put this as a comment, but I don't have enough points to add comments.

Is there a way for "force" the keyboard to dismiss in iOS Swift programmatically?

I have a viewController EditMessage which has two UITextFields (UITextView) which use the keyboard and they work great. This part is basic standard stuff. When the keyboard is displayed, I register a tag gesture for the entire view, so that if the user clicks anywhere else, I dismiss the keyboard:
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(dismissKeyboard)))
In dismissKeyboard, this all works fine:
#objc func dismissKeyboard(sender: Any) {
self.view.endEditing(true)
}
However, I have a menu button(thumbnail image) implemented as a child view controller (UIViewController) on the same EditMessage view, which hijacks the screen via UIApplication.shared.keyWindow() to display an overlay and menu on the bottom of the screen. Built using the model/code from Brian Voong's YouTube channel to replicate a YouTube style slide in menu from the bottom. However, the keyboard is in the way. Since the child is a different view controller "endEditing" doesn't work (or maybe I am referencing the wrong view?).
class ButtonPickerController : UIViewController,
UIGestureRecognizerDelegate, UINavigationControllerDelegate {
var maxSize = CGFloat(60)
let thumbnail: UIImageView = {
let thumbnail = UIImageView()
thumbnail.backgroundColor = UIColor.lightGray
return thumbnail
}()
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.buttonTapped(sender:)))
tap.delegate = self
view.addGestureRecognizer(tap)
//view.backgroundColor = .yellow
view.contentMode = .scaleAspectFit
thumbnail.frame = CGRect(x: 0, y: 0, width: self.maxSize, height: self.maxSize)
setupSubviews()
}
Can someone point me in a good direction? This is my first question so hopefully I am asking properly.
I figured it out in the end. Thank you for the help. In my child view controller I did used the following statement when the button was tapped:
#objc func buttonTapped(sender: UITapGestureRecognizer? = nil) {
self.parent!.view.endEditing(true)
}
First, its not a good way to present overlays as UIViewController.
But a solution good be, to give the second viewcontroller a reference to the first one before viewDidLoad is called. Do you use Segues ? So in prepare would be the right place. In the second viewcontroller you create a property for the first one and then use this property as target when you create the UITapGestureRecognizer.
Another way is using a protocol and delegation.

How to show a customizable view into all view controller?

the question is little bit elaborate what I really want to know.I want to make an app whose some functionality looks like SoundCloud and gaana. Below here I show some screenshot of gaana.
Here if we click any song they automatically update the above view and play the song and also this view will show into all view controller.Clicking this up arrow button how they showing up a new view where the previous small view is no where and also load a collection view into this new view.how to do all of this?the new view is here.
You can do that by creating an overlay, i.e. you add a UIView as a child element to the app's main window like so:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
showOverlay()
}
func showOverlay() {
guard let window = UIApplication.shared.keyWindow else {
//if this block runs we were unable to get a reference to the main window
print("you have probably called this method in viewDidLoad or at some earlier point where the main window reference might be nil")
return
}
//add some custom view, for simplicity I've added a black view
let blackView = UIView()
blackView.backgroundColor = .black
blackView.translatesAutoresizingMaskIntoConstraints = false
blackView.frame = CGRect(x: 0, y: window.frame.height - 100, width: window.frame.width, height: 100.0)
window.addSubview(blackView)
}
Now this view should always be floating above the current UIViewController

Memory stays high after deallocating UINavigationController

I got UINavigationController inside my app with a rootVC "VC1", VC1 containts a collectionview with 2images inside every cell. When user select cell, navigationcontroller will pass images from cell to new vc "VC2" and then push it to the top of navigationcontroller. My problem is when I take down VC2 via popviewcontroller, VC2 is deallocated correctly but memory stays at the same higher level (after pushing new vc it increases from 60mb to 130mb). I've trying set image to nil, and imageview also but none of this work. Here's some of my code:
class VC1: UIViewController {
var selectedUserPollDetails : VC2?
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as! AppUserCell
selectedUserPollDetails = VC2()
selectedUserPollDetails?.leftPhoto = cell.leftImageNode.image
selectedUserPollDetails?.rightPhoto = cell.rightImageNode.image
navigationController?.pushViewController(selectedUserPollDetails !, animated: true)
}
}
class VC2: UIViewController {
lazy var arrow : ArrowBack = {
let arrow = ArrowBack()
return arrow
}()
weak var leftPhoto: UIImage?
weak var rightPhoto: UIImage?
var leftPhotoImageview: UIImageView = {
let imageview = UIImageView()
imageview.contentMode = .scaleAspectFill
imageview.layer.cornerRadius = 5
imageview.layer.masksToBounds = true
return imageview
}()
var rightPhotoImageview: UIImageView = {
let imageview = UIImageView()
imageview.contentMode = .scaleAspectFit
imageview.layer.cornerRadius = 5
imageview.clipsToBounds = true
return imageview
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(leftPhotoImageview)
view.addSubview(rightPhotoImageview)
view.addSubview(arrow)
arrow.addTarget(self, action: #selector(handleArrowBack), for: .touchUpInside)
}
func handleArrowBack(){
navigationController?.popViewController(animated: true)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
leftPhotoImageview.frame = CGRect(x: 100, y: 0, width: 100, height: 100)
rightPhotoImageview.frame = CGRect(x: 100, y: 200, width: 100, height: 100)
if leftPhoto != nil, rightPhoto != nil{
leftPhotoImageview.image = leftPhoto
rightPhotoImageview.image = rightPhoto
}
}
deinit{
leftPhoto = nil
rightPhoto = nil
leftPhotoImageview.image = nil
rightPhotoImageview.image = nil
}
I've even added deinit at the end to make sure photos are deallocated. So basically when i'm trying to push VC2 again (after pop) amount of memory is doubled again (260mb) and so on... what causing this problem? what am i doing wrong?
btw. i've omitted less important func and vars
You defo have a memory leak. I believe everytime
you push a new view via the nav controller it creates a brand new view i.e. the view is completely new and isn't reused. If you have strong references within the view you've pushed to they won't be released unless you deinit as they have a strong ref to view you've pushed so they linger around. You mentioned you deinit those items. Have you also tried marking leftPhotoImageView and rightPhotoImageView also as weak properties? Something is holding onto those images it would seem.
You could also change deinit to leftPhotoImageview = nil and rightPhotoImageView = nil rather than setting the imageview.image property to nil if that makes sense.
Ok, i think i found a solution, i don't know why i haven't trying this one, so i marked my imageview as lazy, and now memory is decreasing after popping vc

Can we get an array of all storyboard views

I'm developping application in Swift.
This application has many view and I would like to put a UIProgressView on all views
Can we get an array of all storyboard views ?
for exemple :
self.progressBar = UIProgressView(progressViewStyle: .Bar)
self.progressBar?.center = view.center
self.progressBar?.frame = CGRect(x: 0, y: 20, width: view.frame.width, height: CGFloat(1))
self.progressBar?.progress = 1/2
self.progressBar?.trackTintColor = UIColor.lightGrayColor();
self.progressBar?.tintColor = UIColor.redColor();
var arrayViewController : [UIViewController] = [...,...,...]
for controller in arrayViewController {
controller.view.addSubview(self.progressBar)
}
Thank you
Ysée
I assume that what you really want is to have the progress displayed on every view IF there is an operation in progress.
There are many ways to do that (using delegation, NSNotificationCenter, …) but the easiest I can think of would be to rely on viewWillAppear
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Check if there's an operation in progress and add progressView if relevant
}
For the user, it will effectively look like you added the progress view to all views.
Why not create a base class that has a lazy stored property of type UIProgressView ? Optionally you can have two methods setProgressViewHidden(hidden : Bool) in order to easily show and hide the progress view and setProgress(progress : Float) to update the progress. Then all your view controllers can subclass this base class and conveniently interact with the progress view.
class ProgressViewController : UIViewController {
lazy var progressView : UIProgressView = {
[unowned self] in
var view = UIProgressView(frame: CGRectMake(0, 20, self.view.frame.size.width, 3))
view.progress = 0.5
view.trackTintColor = UIColor.lightGrayColor()
view.tintColor = UIColor.redColor()
self.view.addSubview(view)
return view
}()
}
To read more about lazy stored properties, check: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html

Resources