Custom TabBar layout for UITabBarViewController - ios

Please refer to this Answer.
I am trying to do the same thing, however I want to do this in a Tab Bar App where the Now Playing bar is above the Tab Bar in all the scenes of the app.
Update:
I want to have a view at the bottom of the screen (above the tab bar) and under the content views of the different tabs (not above them). In addition, I want to have the ability to remove this view at a certain point making the main view take the whole screen.
I can do this using the mentioned Answer by changing the constraints of the nowPlaying view programmatically.

Using UITabBarViewController subclass it is possible:
Ex:
class DashBoardViewController: UITabBarController {
let nowPlayingBar:UIView = {
let view = UIView(frame: .zero)
view.backgroundColor = .blue
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
initView()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
nowPlayingBar.frame = tabBar.frame
}
override func viewDidAppear(_ animated: Bool) {
var newSafeArea = UIEdgeInsets()
// Adjust the safe area to accommodate
// the height of the bottom views.
newSafeArea.bottom += nowPlayingBar.bounds.size.height
// Adjust the safe area insets of the
// embedded child view controller.
self.childViewControllers.forEach({$0.additionalSafeAreaInsets = newSafeArea})
}
private func initView() {
nowPlayingBar.frame = tabBar.frame
view.addSubview(nowPlayingBar)
}
}

You'll add your view/container to your app window, you'd do something like
guard let window = (UIApplication.shared.delegate as? AppDelegate)?.window
else { return } // check if there's a window
let containerHeight: CGFloat = 50 // height for the view where you wish to add the music player
let containerFrame = CGRect(x:0, y: window.frame.maxY - (tabBar.frame.height + containerHeight), width: window.frame.width, height: containerHeight)
// most important part here is the y axis in some sense, you will add the height of the tabBar and the container, then subtract it from window.frame.maxY
let container = UIView(frame: containerFrame)
// now you have the container do whatever you want with it
window.addSubView(container) // finally add the container to window as a subview

Related

UIScrollView with glitch content on iPhone 11 Pro Max

I’m new in Swift and I tried making UIScrollView that shows view controllers.
Every thing perfect just at iPhone 11 Pro Max the next screen show a little bit on the side:
the orange strip is the next screen
My Code:
//MARK: - outlets
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
//MARK: - properties
var viewControllers: [String] = ["ComputerViewController", "AttactViewController", "DefenceViewController", "OfflineViewController"]
var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
//MARK: - life cyrcles
override func viewDidLoad() {
super.viewDidLoad()
for index in 0..<viewControllers.count {
frame.origin.x = scrollView.frame.size.width * CGFloat(index)
frame.size = scrollView.frame.size
let view = UIView(frame: frame)
let storyboard = UIStoryboard(name: "Menu", bundle: nil)
var controller: UIViewController = storyboard.instantiateViewController(withIdentifier: viewControllers[index]) as UIViewController
view.addSubview(controller.view)
self.scrollView.addSubview(view)
}
scrollView.contentSize = CGSize(width: scrollView.frame.size.width * CGFloat(viewControllers.count), height: scrollView.frame.size.height)
scrollView.delegate = self
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
pageControl.currentPage = Int(pageNumber)
}
thanks for helping...
A couple of observations:
You should avoid referencing frame in viewDidLoad. At this point, the frame is not known.
You should avoid hard-coding the size and placement of the subviews at all. There can be a variety of events that change the view’s size later on (e.g. rotations, split view multitasking, etc.). Use constraints.
With scroll views, there are two layout guides, one for its frame and another for its contentSize. So set the size of the subviews using the frameLayoutGuide and the placement of these subviews relative to the contentLayoutGuide.
When you add a view controller’s view as a subview, make sure you call addChild(_:) and didMove(toParent:) calls for view controller containment. See “Implementing a Container View Controller” section of the view controller documentation.
If you want to add paging behavior, just set isPagingEnabled of the scroll view.
Pulling that all together:
override func viewDidLoad() {
super.viewDidLoad()
addChildViews()
}
override func viewDidLoad() {
super.viewDidLoad()
var anchor = scrollView.contentLayoutGuide.leadingAnchor
for identifier in viewControllers {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let child = storyboard.instantiateViewController(withIdentifier: identifier)
addChild(child) // containment call
child.view.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(child.view)
child.didMove(toParent: self) // containment call
NSLayoutConstraint.activate([
// define size of child view (relative to `frameLayoutGuide`)
child.view.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
child.view.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor),
// define placement of child view (relative to `contentLayoutGuide`)
child.view.leadingAnchor.constraint(equalTo: anchor),
child.view.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
child.view.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
])
anchor = child.view.trailingAnchor
}
anchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor).isActive = true
scrollView.isPagingEnabled = true
}
I’ve eliminated the property frame as that’s not needed anymore (and is just a source of confusion with the view controller’s view’s property of the same name). I’ve also eliminated the container view as it didn’t add much to the overall solution (and only adds a layer of constraints to add).
But the key is to use constraints to dictate the size and position of the subviews within the scroll view and to use view controller containment API.

iOS 13 remove UIView for simulate status bar background

I'm working with a UITableViewController which when scrolling makes the navigationBar disappear. Now when the navigation bar is hidden when the user swipes the table view the contents of the cells are seen below the status bar ...
To solve this problem I tried to insert a UIView to simulate a background of the status bar and everything works but the problem is that when I close the UITableViewController the background view of the status bar is not removed from the superview
For now my code is this, can you help me understand where I am wrong? why can't I remove the UIView from the superview?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupStatusBarView()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.navigationBar.isHidden = true
UIApplication.shared.windows.first?.viewWithTag(1)?.removeFromSuperview()
}
//MARK: - Setup Status Bar View
func setupStatusBarView() {
let height = view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0
let statusBarView = UIView()
statusBarView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height:height+5)
statusBarView.backgroundColor = .systemBackground
statusBarView.tag = 1
UIApplication.shared.windows.first?.addSubview(statusBarView)
}
viewDidLayoutSubviews get calls multiple times and you have put setupStatusBarView() in viewDidLayoutSubviews that means your background view has been added multiple times and this is totally wrong flow!
You are removing topmost view only not previous ones!
You should set frame in viewDidLayoutSubviews and should add the view from viewDidLoad!
try this one
let subviewArray = UIApplication.shared.windows.first?.subviews
for view in subviewArray!{
if view.tag == 1{
view.removeFromSuperview()
}
}

Corner radius for Popover view controller

I want to change the corner radius of Popoverview.
Below is my code.
class PopOverViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillLayoutSubviews() {
super.viewDidLayoutSubviews()
self.view.superview?.layer.cornerRadius = 0.0
self.view.superview?.layer.masksToBounds = true
}
}
I am presenting the view controller like below
if let popoverPresentationController = popOverViewController.popoverPresentationController {
popoverPresentationController.permittedArrowDirections = .down
popoverPresentationController.sourceView = tabBar
popoverPresentationController.sourceRect = rect
popoverPresentationController.delegate = self
popoverPresentationController.canOverlapSourceViewRect = false
popOverViewController.preferredContentSize = CGSize(width: 341, height: 68)
self.present(popOverViewController, animated: true, completion: {
})
}
It always shows rounded corners.
Any help is appreciated.
As of right now, there is no supported way to have a popover view controller with unrounded corners. The way that UIPopoverController works is by adding your view to a view with rounded corners that clips to bounds.
There are two ways to go around this:
Wait for popover controller to be shown then traverse its parents and set radius to 0 and clips to bounds to false. This is kind of hacky and it it might not be compatible with all versions of iOS past and future. I do not recommend this.
Create your own class that mimics the same functionality as UIPopoverController. This is the best way to solve your problem.

frame of navigationitem titleView in viewcontroller view

How do I get the frame of a navigationItem's titleView in the coordinate system of the viewcontroller's view?
if let navBarHeight = navigationController?.navigationBar.frame.height,
let navBarWidth = navigationController?.navigationBar.frame.width {
myCustomTitleView.frame = CGRect(x: 0, y: 0, width: navBarWidth, height: navBarHeight)
navigationItem.titleView = myCustomTitleView
}
However, when I check myCustomTitleView's frame origin, I get (0, 0).
I then tried to translate this origin to the viewcontroller's view. what I got was (0,-44), which accounts for the navigation bar height but not for the x-offset.
let originInVCView = view.convert(myCustomTitleView.frame.origin, from: myCustomTitleView)
This can't be right as the titleView obviously has an offset (space for the back button).
How do I correctly extract the translated titleView origin?
You want to make sure you have set the navigation item in viewDidLoad() first. Otherwise it will be nil.
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView(image: UIImage(named: "MY_IMAGE"))
navigationItem.titleView = imageView
When done you can get the frame in the VC's viewDidAppear where the view has been laid out:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let nItemFrame = navigationItem.titleView?.frame //<<<---
}

Customise Position of UIPageControl

I am using project from github as a reference.
project URL:
https://github.com/lephuocdai/iOSsample/tree/master/PageViewDemo
In this project i want to show the UIPageControl at top left position .
I tried setting the rect property of pageControl using CGRectMake() to some value ;But it shows always at bottom center
here s a very neat and 100% effective way to get to change the position of the pageControl
extension UIPageViewController {
override open func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for subV in self.view.subviews {
if type(of: subV).description() == "UIPageControl" {
let pos = CGPoint(x: newX, y: newY)
subV.frame = CGRect(origin: pos, size: subV.frame.size)
}
}
}
}
The project uses a UIPageViewController to handle the display and movement through the content.
You can supply data to that object so it displays a UIPageControl as you say.
BUT you have no control over the display of that item, other than maybe some colour styling.
If you want to position it, you'll need to implement you're own instance of UIPageControl and handle it's content, position and changes manually.
Override the viewDidLayoutSubviews() of the pageviewcontroller and use this
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// get pageControl and scroll view from view's subviews
let pageControl = view.subviews.filter{ $0 is UIPageControl }.first! as! UIPageControl
let scrollView = view.subviews.filter{ $0 is UIScrollView }.first! as! UIScrollView
// remove all constraint from view that are tied to pagecontrol
let const = view.constraints.filter { $0.firstItem as? NSObject == pageControl || $0.secondItem as? NSObject == pageControl }
view.removeConstraints(const)
// customize pagecontroll
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.addConstraint(pageControl.heightAnchor.constraintEqualToConstant(35))
pageControl.backgroundColor = view.backgroundColor
// create constraints for pagecontrol
let leading = pageControl.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor)
let trailing = pageControl.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor)
let bottom = pageControl.bottomAnchor.constraintEqualToAnchor(scrollView.topAnchor, constant:8) // add to scrollview not view
// pagecontrol constraint to view
view.addConstraints([leading, trailing, bottom])
view.bounds.origin.y -= pageControl.bounds.maxY
}
is your page control encompassed within some other View, if so then you may be setting the co-ordinates wrong, try to put log of your page control.frame so as to know where it lies
If using storyboards, place a UIPageControl object using the menu in the bottom right and set constraints.
If using frames, just programmatically add it:
var pageControl = UIPageControl()
pageControl.frame = CGRectMake(0,0,0,0) <- These are the coordinates.
self.view.addSubView(pageControl)
if you set the frame for UIPageControl , it doesnot work.
Other than this you can set the transform.
[_pageControl setTransform:CGAffineTransformMakeTranslation(100, 0.0)];
enjoy coding

Resources