Corner radius for Popover view controller - ios

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.

Related

systemLayoutSizeFitting not returning proper size of view

I'm setting a view controller's view as my UITableView's header.
var headerView = CommunityPostDetailTableHeaderViewController()
override func viewDidLoad() {
// other stuffs
headerView.view.frame = CGRect(x: 0, y: 0, width: self.tableView.frame.size.width, height: 100)
headerView.delegate = self
self.tableView.tableHeaderView = headerView.view
}
And using this bit of code to resize it according to the size of view.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFitting(CGSize(width: tableView.bounds.width, height: 0)).height
var headerFrame = headerView.frame
// Comparison necessary to avoid infinite loop
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}
I'm using this technique for two of my table views.
tableview is presented directly from a view controller like so :
let playerController = VideoDetailController()
playerController.modalPresentationStyle = .fullScreen
playerController.video = video
self.present(playerController, animated: true, completion: nil)
Working fine in all iOS devices.
the other is presented by embedding inside a navigation controller :
let communityPostDetailVC = CommunityPostDetailViewController()
communityPostDetailVC.delegate = self
if let indexpath = indexpath {
communityPostDetailVC.communityPost = datasource[indexpath.row]
communityPostDetailVC.indexpath = indexpath
}
let navigationController = UINavigationController(rootViewController: communityPostDetailVC)
navigationController.modalPresentationStyle = .fullScreen
self.present(navigationController, animated: true)
Not resizing properly on iPhone 5/5s/SE(1st gen)/6/6s/7/8/SE(2nd gen).
I can't figure out why it is not working on smaller phones. You can see blank space in the comparison attachment below. In smaller SE the space is even more.
Any suggestions/ideas are welcome. I'm clueless at this point.
PS: I've nested view controller's. the headerView is a view controller holding another view controller's view. The FB logo and the pink label underneath is part of the nested view controller. Other than that everything else is in headerView's view controller's view.
you are using fullscreen , but you image is a fixed size image . pink colour view also fixed size.
maybe its making a problem .
its actually not an answer. a suggestion for you. you may review again your code. hope you will find .
happy coding

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()
}
}

Custom TabBar layout for UITabBarViewController

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

Mask in UITableViewCell subclass not properly rendering on first load

I am trying to create a UITableViewCell subclass containing two rounded views, one on top and one on bottom, that together end up as a rounded rectangular view inside the cell, with indented space on all 4 sides (set by auto layout constrains in the storyboard for the prototype cell). These cells are part of a tableview that is loaded into a UIContainerView which has its contents swapped out based on the selection of a selection control.
Here is what I want the cell to look like (blacked out):
Here is what it looks like briefly, when first loading:
Here is what it looks like after it first loads:
When I switch to a different tab, then come back, it renders the cell correctly.
I use this method in the parent view controller (adapted from this)
func cycleFromViewController(oldViewController: UIViewController, toViewController newViewController: UIViewController) {
oldViewController.willMoveToParentViewController(nil)
self.addChildViewController(newViewController)
self.addSubView(newViewController.view, toView:self.containerView!)
newViewController.view.alpha = 0
newViewController.view.layoutIfNeeded()
UIView.animateWithDuration(0.25, animations: {
newViewController.view.alpha = 1
oldViewController.view.alpha = 0
},
completion: { finished in
oldViewController.view.removeFromSuperview()
oldViewController.removeFromParentViewController()
newViewController.didMoveToParentViewController(self)
})
}
The parent view controller's viewDidLoad method is called like this:
override func viewDidLoad() {
... // grab data in a background network call, populating the array of model objects
self.currentSelectedViewController!.view.translatesAutoresizingMaskIntoConstraints = false
self.addChildViewController(self.currentSelectedViewController!)
self.addSubView(self.currentSelectedViewController!.view, toView: self.containerView)
self.refreshContainerView()
super.viewDidLoad()
}
refreshContainerView looks like this:
func refreshContainerView() {
let currentVC = self.currentSelectedViewController as! MyTableViewController
currentVC.modelObjectList = self.modelObjectList
self.label.hidden = true
self.button.hidden = true
currentVC.tableView.reloadData()
}
Here is my cell's layout subviews method:
override func layoutSubviews() {
super.layoutSubviews()
self.reminderView.backgroundColor = UIColor.grayColor()
if let aModel = self.model {
self.configureWithModel(aModel)
}
self.setMaskToView(self.topView, corners: UIRectCorner.TopLeft.union(UIRectCorner.TopRight))
self.setMaskToView(self.bottomView, corners: UIRectCorner.BottomLeft.union(UIRectCorner.BottomRight))
}
Any thoughts as to how to fix
1. the initial brief loading without the insets and
2. the final rendering of the initial load with the rounded corners on the right side not properly rendering?
This cell exists in a storyboard as a prototype, with the insets created via auto layout constraints. (a constant setting the top and bottom view's distance from the top, bottom, right and left as appropriate). Clearly these constraints work when the cell is reloaded, but not on the initial load for some reason that is escaping me.
Evidently the answer was fairly simple. The mask method was being called in layoutSubviews for the cell, the the views themselves did not yet have their bounds set. So I subclassed the view into a new RoundedView class, and added a var for the corners and a modified mask method:
class RoundedView: UIView {
var corners : UIRectCorner = []
override func layoutSubviews() {
self.setMaskForCorners(corners)
}
func setMaskForCorners(corners: UIRectCorner) {
let rounded = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: 10, height: 10))
let mask = CAShapeLayer()
mask.path = rounded.CGPath
self.layer.mask = mask
}
}
Then I changed the views to be that subclass and then call it like this:
self.topView.corners = UIRectCorner.TopLeft.union(UIRectCorner.TopRight)
self.bottomView.corners = UIRectCorner.BottomLeft.union(UIRectCorner.BottomRight)

Why doesn't defining my view controller size with frameOfPresentedViewInContainerView make it that size?

I've been following this tutorial on implementing custom view controller transitions in iOS 8 with UIPresentationController, and so far it all makes sense, but I can't seem to get my view controller to be the right size.
In that tutorial, they have the following code:
class OverlayPresentationController: UIPresentationController {
let dimmingView = UIView()
override init(presentedViewController: UIViewController!, presentingViewController: UIViewController!) {
super.init(presentedViewController: presentedViewController, presentingViewController: presentingViewController)
dimmingView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
}
override func presentationTransitionWillBegin() {
dimmingView.frame = containerView.bounds
dimmingView.alpha = 0.0
containerView.insertSubview(dimmingView, atIndex: 0)
presentedViewController.transitionCoordinator()?.animateAlongsideTransition({
context in
self.dimmingView.alpha = 1.0
}, completion: nil)
}
override func dismissalTransitionWillBegin() {
presentedViewController.transitionCoordinator()?.animateAlongsideTransition({
context in
self.dimmingView.alpha = 0.0
}, completion: {
context in
self.dimmingView.removeFromSuperview()
})
}
override func frameOfPresentedViewInContainerView() -> CGRect {
return containerView.bounds.rectByInsetting(dx: 30, dy: 30)
}
override func containerViewWillLayoutSubviews() {
dimmingView.frame = containerView.bounds
presentedView().frame = frameOfPresentedViewInContainerView()
}
}
I understand all of it, except for frameOfPresentedViewInContainerView. That returns a size, but, if I remove presentedView().frame = frameOfPresentedViewInContainerView() in containerViewWillLayoutSubviews it doesn't work. Why do I have to have that line? You would think the function itself would be sufficient, otherwise I'd just implement a random size in the containerViewWillLayoutSubviews method.
The frameOfPresentedViewInContainerView is used by UIKit to get the initial presentedView's frame that is then passed to an animator (conforming to UIViewControllerAnimatedTransitioning protocol) so that it knows the target position of the presented view when setting up an animation. After the presenting animation finishes and the presentation is up the screen or one of the parent view controllers may still resize because of a rotation or a size class change. UIPresentationController instance has a chance to respond to these changes in containerViewWillLayoutSubviews method and resize the presentedView appropriately.
In other words the presentation controller is always responsible for determining the layout for the presented view, but initially it just tells UIKit what the frame is so that the animator can use this information but after that the presentation controller sets the frame on the presented view directly.
You only need to have that line if you want the presented view frame to be updated when the screen rotates.

Resources