I'm trying to implement a MDCBottomNavigationBar which contains a MDCTabBarView, which in turn contains 2 other views (shown as tabbed).
In my main controller I create the 4 controllers for the menu items at the bottom and implement MDCTabBarViewDelegate. When the user taps on a bottom menu item, they are presented with the corresponding controller. Now I want one of the controllers to contain a tab bar at the top, as shown in the demo image below. I have created the same MDCTabBarViewDelegate with 2 controllers and added them to the view. They are shown and they are switching whenever I change the tabs, but there are issues with how it looks. So, Tab 2 should contain the tab bar with App and All, and underneath the tab bar, the content of each tab should be shown.
Please check this image:
Simulator Demo Image
The Green area should actually be the content of Tab 2 -> App. The problem is the Green area is displayed on the entire screen, instead of being displayed under the tab bar. The second problem is that my view controllers are showing a faded bar above the bottom menu. I tried to hide that bar with navigationController?.navigationBar.isHidden = true but nothing seems to work.
Can someone help me with the 2 issues above? I can't find a single resource / documentation about how to use this component on iOS. It would be great if you could even share some docs that I can read by myself, if not the answer.
Parent view controller:
class HomeController: UITabBarController {
let firstC = FirstController()
let secondC = SecondController()
let thirdC = ThirdControllerController()
let fourthC = FourthController()
let bottomNavBar: MDCBottomNavigationBar = {
let bottomNavBar = MDCBottomNavigationBar()
bottomNavBar.titleVisibility = .always
bottomNavBar.alignment = .justifiedAdjacentTitles
return bottomNavBar
}()
let firstItem: MDCTabBarItem = {
let firstItem = MDCTabBarItem()
firstItem.title = "Tab 1"
firstItem.image = UIImage(systemName: "house.fill")
firstItem.tag = 0
return firstItem
}()
let secondItem: MDCTabBarItem = {
let secondItem = MDCTabBarItem()
secondItem.title = "Tab 2"
secondItem.image = UIImage(systemName: "house.fill")
secondItem.tag = 1
return secondItem
}()
let thirdItem: MDCTabBarItem = {
let thirdItem = MDCTabBarItem()
thirdItem.title = "Tab 3"
thirdItem.image = UIImage(systemName: "house.fill")
thirdItem.tag = 2
return thirdItem
}()
let fourthItem: MDCTabBarItem = {
let fourthItem = MDCTabBarItem()
fourthItem.title = "Tab 4"
fourthItem.image = UIImage(systemName: "house.fill")
fourthItem.tag = 3
return fourthItem
}()
override func viewDidLoad() {
super.viewDidLoad()
bottomNavBar.delegate = self
bottomNavBar.items = [firstItem, secondItem, thirdItem, fourthItem]
self.viewControllers = [
firstC,
secondC,
thirdC,
fourthC
]
view.addSubview(bottomNavBar)
}
}
// SecondC - inner controller
class SecondController: UITabBarController {
let firstC = TabFirstC()
let secondC = TabSecondC()
let firstTab: MDCTabBarItem = {
let tab = MDCTabBarItem(title: "App", image: nil, tag: 0)
return tab
}()
let secondTab: MDCTabBarItem = {
let tab = MDCTabBarItem(title: "All", image: nil, tag: 1)
return tab
}()
let tabs: MDCTabBarView = {
let tabs = MDCTabBarView()
tabs.translatesAutoresizingMaskIntoConstraints = false
return tabs
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(tabs)
viewControllers = [firstC, secondC]
tabs.tabBarDelegate = self
tabs.items = [firstTab, secondTab]
tabs.selectedItem = firstTab
NSLayoutConstraint.activate([
tabs.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
tabs.topAnchor.constraint(equalTo: logoImageView.safeAreaLayoutGuide.bottomAnchor, constant: 10),
tabs.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
])
}
}
Related
I want to update Swift UI View according to the communication result.
But UIHostingController.view is not fit rootView size at iOS 13.
The same thing happens when I try with the sample code below.
I want to add self-sizing SwiftUI View to UIStackView, but SwiftUI View overlaps with the previous and next views is occurring because this problem.
How can I avoid this problem?
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let object = SampleObject()
let sampleView = SampleView(object: object)
let hosting = UIHostingController(rootView: sampleView)
hosting.view.backgroundColor = UIColor.green
addChild(hosting)
view.addSubview(hosting.view)
hosting.view.translatesAutoresizingMaskIntoConstraints = false
hosting.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
hosting.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
hosting.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
hosting.didMove(toParent: self)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
object.test()
}
}
}
struct SampleView: View {
#ObservedObject var object: SampleObject
var body: some View {
VStack {
Text("test1").background(Color.blue)
Text("test2").background(Color.red)
if object.state.isVisibleText {
Text("test2").background(Color.gray)
}
}
.padding(32)
.background(Color.yellow)
}
}
final class SampleObject: ObservableObject {
struct ViewState {
var isVisibleText: Bool = false
}
#Published private(set) var state = ViewState()
func test() {
state.isVisibleText = true
}
}
If addSubview to UIStackView as below, the height of Swift UI View will not change in iOS13.
iOS13 (incorrect)
iOS14 (correct)
You have not set the bottom anchor, add this line
hosting.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
Another easy way is to set frame to hosting controller view and remove the constraint.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let object = SampleObject()
let sampleView = SampleView(object: object)
let hosting = UIHostingController(rootView: sampleView)
hosting.view.frame = UIScreen.main.bounds //<---here
hosting.view.backgroundColor = UIColor.green
addChild(hosting)
view.addSubview(hosting.view)
hosting.didMove(toParent: self)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
object.test()
}
}
}
Only for iOS 13
Try this:
Every time the view size change, call this:
sampleView.view.removeFromSuperview()
let sampleView = SampleView(object: object)
let hosting = UIHostingController(rootView: sampleView)
view.addArrangedSubview(hosting.view)
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.
I have a view (UIPopoverPresentation) which functionality works fine, but I need to add a custom border. I'm currently using a borderWidth and borderColor but I cannot seem to find a way to make a customized border, as seen in the photo below. How do I go about creating this customized border? Make a CGRect?
What I need:
What I have:
I've attempted to add an image to the background of the popover and it resulted in this:
EDIT: //PopOverView (presented using UIPopOverPresentation)
override func viewDidLoad() {
super.viewDidLoad()
self.view.layer.cornerRadius = 10.0
self.view.layer.borderWidth = 1.5
self.view.layer.borderColor = UIColor.whiteColor().CGColor
self.navigationController?.navigationBarHidden = true
self.popViewTableView.delegate = self
self.popViewTableView.dataSource = self
self.popViewTableView.alwaysBounceVertical = false
self.popViewTableView.backgroundColor = UIColor(red: 151.0/255.0, green: 87.0/255.0, blue: 172.0/255.0, alpha: 1.0)
}
//Base View Controller. When button is pressed, this function is called which presents the popover
func presentPopOver() {
let contentView = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("popViewController") as! DeckPopViewController
contentView.modalPresentationStyle = UIModalPresentationStyle.Popover
contentView.preferredContentSize = CGSizeMake(deckSelectionCGRect.width, 160)
let popoverMenuViewController = contentView.popoverPresentationController!
popoverMenuViewController.delegate = self
popoverMenuViewController.sourceView = view
popoverMenuViewController.permittedArrowDirections = UIPopoverArrowDirection(rawValue:0)
popoverMenuViewController.sourceRect = CGRectMake((self.view.bounds.width/2) - (deckSelectionCGRect.width/2), 120, deckSelectionCGRect.width, deckSelectionCGRect.height)
presentViewController(contentView, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
//ALSO: On the third image, I placed the border imageview over the entire view controller in the storyboard, set padding to zero on all sides (autolayout). It doesn't look good, though.
in my table view controller it works fine and I can configure the view like so:
func configureView() {
self.tableView.backgroundView = UIImageView(image: UIImage(named: "background.jpg"))
//set custom height for tableview row
tableView.rowHeight = 64
//change the font and size of nav bar text
if let navBarFont = UIFont(name: "HelveticaNeue-Thin", size: 20.0) {
let navBarAttributesDictionary: [String: AnyObject]? = [NSForegroundColorAttributeName: UIColor.blackColor(), NSFontAttributeName: navBarFont]
navigationController?.navigationBar.titleTextAttributes = navBarAttributesDictionary
}
}
When I am trying to configure the view in another view controller (with the same navigation controller) it doesn't work and won't change the background or nav bar font, i use this configureview:
func configureView() {
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "background.jpg")!)
//change the font and size of nav bar text
if let navBarFont = UIFont(name: "HelveticaNeue-Thin", size: 20.0) {
print("fired")
let navBarAttributesDictionary: [String: AnyObject]? = [NSForegroundColorAttributeName: UIColor.blackColor(), NSFontAttributeName: navBarFont]
UINavigationBar.appearance().tintColor = UIColor.redColor()
navigationController?.navigationBar.titleTextAttributes = navBarAttributesDictionary
}
}
Does anyone have any suggestions on how to debug this or what could be going on? My first view controller is a table view, second a normal view. Thanks
Edit: my segue is of type Show, and my prepare for segue is as follows:
if segue.identifier == "showAlertInfoSegue" {
if let viewController = segue.destinationViewController as? AlertViewController {
viewController.tableAlert = nil
//if the sender is the selected table view row - load in the data from the data struct
if sender?.tag != 1 {
viewController.tableAlert = alertDetail
}
viewController.onDataAvailable = {[weak self]
(data) in
if let weakSelf = self {
weakSelf.doSomethingWithData(data)
}
}
viewController.hidesBottomBarWhenPushed = true
}
}
I also see a peculiar grey shadow appearing on the top nav bar when transitioning, then it dissapears
my label and toolbar disappear from the view when i run the app hidden behind my image
this is my viewdidload code
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var tiger1 = Tigers()
tiger1.name = "BengalTiger"
tiger1.age = 1
tiger1.breed = "Bengal"
tiger1.fact = "live in the bengal area"
tiger1.image = UIImage(named: "BengalTiger.jpg")!
self.nameLabel.text = tiger1.name
self.ageLabel.text = String(tiger1.age)
self.breedLabel.text = tiger1.breed
self.factLabel.text = tiger1.fact
self.imageView.image = tiger1.image
tigersArray.append(tiger1)
}
and this is my struct:
struct Tigers {
var image = UIImage(named: "")
var name = ""
var age = 0
var breed = ""
var fact = ""
}
Natives metods
insertSubview:(UIView *) aboveSubview:(UIView *)
or
insertSubview:(UIView *) belowSubview:(UIView *)
You can use:
self.view.sendSubviewToBack(imageView)
and that will put it behind your other views
Here you see the imageView in the back of the other view elements, so the button and label will be shown on top of the image view:
Below you see the elements are behind the image view so they will be covered up by the image.
You can arrange elements in the document outline by dragging them up and down.