UINavigation with UITextField as subview - ios

I want to make UI like following,
I tried to increase the height of UINavigationController like
[self.navigationController.navigationBar setFrame:CGRectMake(0, 0, 320, 110)];
but navigation title and BarButtonItem appears down.
i can not add hide navigation as i am using third party side menu which uses Left BarButtonItem.
i can very well keep navigation as it is and below i have added uiview as subview with same background colour as that of navigation but there is fine line appears in between...:(

You can't increase height of UINavigationBar. Add an UIView with those elements just below the UINavigationBar.
Then there will be extra line below UINavigationBar. You can remove that line. Refer How to hide iOS7 UINavigationBar 1px bottom line
or else use this code(I have just copied it from that link in swift, convert it to Objective-C)
import UIKit
class ViewController: UIViewController {
private var shadowImageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if shadowImageView == nil {
shadowImageView = findShadowImage(under: navigationController!.navigationBar)
}
shadowImageView?.isHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
shadowImageView?.isHidden = false
}
private func findShadowImage(under view: UIView) -> UIImageView? {
if view is UIImageView && view.bounds.size.height <= 1 {
return (view as! UIImageView)
}
for subview in view.subviews {
if let imageView = findShadowImage(under: subview) {
return imageView
}
}
return nil
}
}
This is the result

Actually you cant change height of UINavigationBar. Your actual ViewController's view will start from 64 pixel from TopLayoutGuide (Vertical Space)
Status Bar Height is 20 Pixel
Navigation Bar Height is 44 Pixel
So to design such UI, simply do these steps
Hide NavigationBar on that view controller like
self.navigationController.navigationBarHidden = YES;
self.navigationItem.hidesBackButton = YES;
Then add View from storyboard with vertical space as 0 from TopLayoutGuide. And design it as per your requirement. So you can
simply give this look and handle it using IBOutlets and IBActions
for each item
Also you can add UILable to show title of screen.

You can't increase the size of the UINavigationBar. That is of standard size 44 (Nav Height) + 20 (size of the status bar). Here you've two options to achieve.
First, by adding menu, dashboard title label & notification icon to the UINavigationBar. Add Jobs, Conditions, and Interview as a separate view below to navigation bar.
Second by customizing the entire view, as mentioned by #Pushkraj i.e, by hiding the current Navigation Bar and adding your own view which contains all the UI elements.

Related

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

Shrink UITableView -> Black Background

I create a UIDatePicker outside of the screen and when a button is pressed, I want it to slide into the screen (from below).
To give it enough place, I want the UITableView (inside UITableViewController) to shrink over the y-axis.
It kind of works, but the shrinking of the UITableView reveals a black background, so that the UIDatePicker is not shown. How can I fix that?
It looks like this:
import UIKit
class jSONTableViewController: UITableViewController {
#IBAction func viewDatePicker(sender: UIButton) {
// Create DatePicker
let datePicker = UIDatePicker()
datePicker.center.y = self.view.bounds.height+100
datePicker.backgroundColor = UIColor.whiteColor()
datePicker.datePickerMode = UIDatePickerMode.Date
self.view.insertSubview(datePicker, aboveSubview: self.view)
// Animate View
UIView.animateWithDuration(0.5, delay: 0, options: .CurveEaseInOut, animations: { () -> Void in
let heightOfDatepicker = datePicker.frame.height
datePicker.center.y = self.view.frame.height-(heightOfDatepicker)
self.tableView.frame = CGRectMake( self.tableView.frame.origin.x, self.tableView.frame.origin.x, self.tableView.frame.size.width, self.tableView.frame.height-heightOfDatepicker)
}, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You are using a UITableViewController where the super view is the table view. So when you resize the table view you also rezise the super view which shows the window's background.
You can create a UIViewController and add a table view as a subview, you'll need conform to UITableViewDelegate and UITableViewDataSource too.
I think your best bet would be to use the first responder to bring up a picker from the bottom. Most of this is all done for you, you just need to do some wiring. This gives you the equivalent of how the keyboard works but with your picker instead and optional buttons.
Discussion here today on how best to do this: Add buttons to UIPickerView - Swift 1.2
This has some swift code you can perhaps just lift and use.

UITextView is not scrolled to top when loaded

When I have text that does not fill the UITextView, it is scrolled to the top working as intended. When there is more text than will fit on screen, the UITextView is scrolled to the middle of the text, rather than the top.
Here are some potentially relevant details:
In viewDidLoad to give some padding on top and bottom of UITextView:
self.mainTextView.textContainerInset = UIEdgeInsetsMake(90, 0, 70, 0);
The UITextView uses auto layout to anchor it 20px from top, bottom and each side of the screen (done in IB) to allow for different screen sizes and orientations.
I can still scroll it with my finger once its loaded.
EDIT
I found that removing the auto layout constraints and then fixing the width only seems to fix the issue, but only for that screen width.
add the following function to your view controller class...
Swift 3
override func viewDidLayoutSubviews() {
self.mainTextView.setContentOffset(.zero, animated: false)
}
Swift 2.1
override func viewDidLayoutSubviews() {
self.mainTextView.setContentOffset(CGPointZero, animated: false)
}
Objective C
- (void)viewDidLayoutSubviews {
[self.mainTextView setContentOffset:CGPointZero animated:NO];
}
UITextView is a subclass of UIScrollView, so you can use its methods. If all you want to do is ensure that it's scrolled to the top, then wherever the text is added try:
[self.mainTextView setContentOffset:CGPointZero animated:NO];
EDIT: AutoLayout with any kind of scrollview gets wonky fast. That setting a fixed width solves it isn't surprising. If it doesn't work in -viewDidLayoutSubviews then that is odd. Setting a layout constraint manually may work. First create the constraints in IB:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewWidthConstraint;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewHeightConstraint;
then in the ViewController
-(void)updateViewConstraints {
self.textViewWidthConstraint.constant = self.view.frame.size.width - 40.0f;
self.textViewHeightConstraint.constant = self.view.frame.size.height - 40.0f;
[super updateViewConstraints];
}
May still be necessary to setContentOffset in -viewDidLayoutSubviews.
(Another method would be to create a layout constraint for "'equal' widths" and "'equal' heights" between the textView and its superView, with a constant of "-40". It's only 'equal' if the constant is zero, otherwise it adjusts by the constant. But because you can only add this constraint to a view that constraints both views, you can't do this in IB.)
You may ask yourself, if I have to do this, what's the point of AutoLayout? I've studied AutoLayout in depth, and that is an excellent question.
Swift
self.textView.scrollRangeToVisible(NSMakeRange(0, 0))
Objective-C
[self.textView scrollRangeToVisible:(NSMakeRange(0, 0))];
i had same issue! Reset to suggested constrains and just put (y offset)
#IBOutlet weak var textContent: UITextView!
override func viewDidLoad() {
textContent.scrollsToTop = true
var contentHeight = textContent.contentSize.height
var offSet = textContent.contentOffset.x
var contentOffset = contentHeight - offSet
textContent.contentOffset = CGPointMake(0, -contentOffset)
}
For iOS9 and later the textview even on viewWillAppear: is coming with CGRect(0,0,1000,1000). In order for this to work you have to call in viewWillAppear:
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
// * Your code here
After that the textview will have correct CGRect data and you can perform any scrolling operation you may need.
The problem with putting code in viewDidLayoutSubviews and viewWillLayoutSubviews is that these methods are called a lot (during device rotation, resizing views etc ...). If you're reading something from text view, and you rotate the device, you expect that the part of the content you're viewing stays on screen. You do not expect that it scrolls back to top.
Instead of scrolling the content to top, try to keep text view's scrollEnabled property set to NO (false), and turn it back on in viewDidAppear.
If you don't wanna mess with constraints:
override func updateViewConstraints() {
super.updateViewConstraints()
}
override func viewDidLayoutSubviews() {
self.textLabel.setContentOffset(CGPointZero, animated: false)
}
This is an interesting bug. In our project, this is only occurring on devices with an iPhone 5-size screen. It appears that the textview contentOffset changes at some point during the view controller lifecycle. In viewDidLoad and viewWillAppear the textview's contentOffset is 0,0, and by viewDidAppear it's changed. You can see it happening in viewWillLayoutSubviews. Constraints appear to be set up correctly.
This will ensure you don't call a scrolling method unless it's needed:
if textView.contentOffset.y > 0 {
textView.contentOffset = CGPoint(x: 0, y: 0)
// Or use scrollRectToVisible, scrollRangeToVisible, etc.
}
Swift
override func viewDidLoad() {
super.viewDidLoad()
textView.isScrollEnabled = false
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
textView.isScrollEnabled = true
}
For me this works in a different way, I tried all things mentioned above but none of the worked in func viewWillAppear(_ animated: Bool). Which eventually makes textView scrolled up, and in func viewDidAppear(_ animated: Bool) it would scroll after screen appeared.
Below worked for me but got some constraint related issue with keyboard up and down.
override func viewDidLayoutSubviews() {
self.textView.setContentOffset(.zero, animated: false)
}
Below worked as expectation:
override func viewDidLoad() {
super.viewDidLoad()
self.textView.scrollsToTop = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutIfNeeded()
self.textView.setContentOffset(.zero, animated: false)
}
David Rectors answer in Objective C:
#import "TopTextView.h"
#implementation TopTextView
bool scrolled = NO;
- (void) layoutSubviews
{
[super layoutSubviews];
if (!scrolled) {
[self setContentOffset:CGPointMake(0, 0) animated:NO];
scrolled = YES;
}
}
#end
It seems like a terrible idea to handle this issue in code in the view controller because: A. The view controller isn't making any mistake or doing anything wrong, and B, if you have more than one view controller with a wrongly scrolled text view, you end up with redundant code. The solution should be to write code that exists in the text view class. My solution works with Interface Builder where I simply select a custom class for the UITextView and use this class:
import Foundation
import UIKit
class TopTextView: UITextView {
var scrolled = false
override func layoutSubviews() {
super.layoutSubviews()
if scrolled { return }
setContentOffset(.zero, animated: false)
scrolled = true
}
}
This worked for me. I happen to have a view controller with a child view with a UITextView as a child of that view, not with a UITextView as the child of the view controller. I don't know how well this works if the text view is under top or bottom bars but since no edge insets are touched, this should work.
In my case I had to do it like this:
textView.setContentOffset(CGPoint(x: 0, y: -self.textView.adjustedContentInset.top), animated: false)
because the texview was underneath the navigation bar and had an adjusted inset

Is it possible to pin a UIView's top to the bottom of the navigation bar?

I'm trying to position my UIView to be 20pt under the navigation bar, but when I set it relative to the view on the view controller it's still under the navigation bar at 20pt, and I don't want to hardcode it.
Is it possible to position it away from the navigation bar?
To do this programmatically use the topLayoutGuide of your view controller:
override func viewDidLoad() {
...
myView.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor).isActive=true
...
}
Try adding contraint with TopLayoutGuide,
The main difference between TopLayoutGuide and self.view is, top layout guide starts with bottom of status bar + bottom of navigation bar(if exist), but self.view always start from (0,0) coordinate in iOS 7 for translucent navigation bar.
So in your case you should try pinning from top layout guide.
From iOS 11.0 programmatically you can use view.safeAreaLayoutGuide:
override func viewDidLoad() {
...
label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive=true
}
Pin to the top safe area layout guide. Code if your are using SnapKit:
titleLabel.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top).offset(20)
make.leading.equalToSuperview()
}
So your UIView sits within a UIViewController, correct?
When you define your initial inner UIView to that UIViewController, can you set its coordinates to be 20px off the top of the ViewController?
For example:
//
// ViewControllerEx.swift
//
import UIKit
class ViewControllerExample: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var innerContainer = UIView(frame: CGRectMake(0, 20, self.view.bounds.width, self.view.bounds.height-20))
self.view.addSubview(innerContainer)
}
}

TableView Showing Behind Tab Bar

I am updating my app to use iOS 7 and I'm having a problem with a table view. My tab bar is translucent. The problem is when I scroll to the bottom of my table view, part of the last cell is still behind the tab bar. I'd like to have a bit of space between the last cell and the tab bar. I could fix this by using an opaque tab bar instead, but I want to keep it translucent.
Try setting
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = NO;
self.automaticallyAdjustsScrollViewInsets = NO;
Inside the tableview controller
Swift 4.x
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(0, 0, self.tabBarController!.tabBar.frame.height, 0)
self.yourTableView.contentInset = adjustForTabbarInsets
self.yourTableView.scrollIndicatorInsets = adjustForTabbarInsets
Check the screen shot
Check the under top Bar and Un-checke under Bottom Bar
SWIFT 3
put this inside viewDidLoad of your tableViewController:
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
self.automaticallyAdjustsScrollViewInsets = false
Swift 3.0
This is what worked for me. In your Custom ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(self.tabBarController!.tabBar.frame.height, 0, 0, 0);
//Where tableview is the IBOutlet for your storyboard tableview.
self.tableView.contentInset = adjustForTabbarInsets;
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets;
}
Not to sure I like the solution but it works for me.
With iOS 11 I have no issue, I simply use the following in viewDidLoad():
self.collectionView.bottomAnchor.constraint(self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
However on iOS 10 I need to hack my way like this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let tabBarHeight: CGFloat = (self.parent?.tabBarController?.tabBar.frame.size.height)!
if #available(iOS 11.0, *) {
} else {
self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -tabBarHeight).isActive = true
}
}
This is working for me
override func viewDidLoad() {
self.edgesForExtendedLayout = UIRectEdge()
self.extendedLayoutIncludesOpaqueBars = false
}
If any view shows behind a UITabBar you can grab the bottomLayoutGuide and make adjustments at runtime. What I do is have a BaseViewController that all my view controllers inherit from. Then if the tab bar is visible we adjust the view like so:
import UIKit
class BaseVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
//Ensures that views are not underneath the tab bar
if tabBarController?.tabBar.hidden == false {
var viewBounds = self.view.bounds;
var bottomBarOffset = self.bottomLayoutGuide.length;
self.view.frame = CGRectMake(0, 0, viewBounds.width, viewBounds.height - bottomBarOffset)
}
}
}
Since I don't use storyboards (where you can click a checkbox in IB to fix this problem), this has been the best solution I have found.
It is really hard to resolve the issue without detail information or actual codes. I have similar issue of tabview behind UItabBar in my project. The solutions offered here do not work in my case. After exploring my codes, I found a solution for my case.
Here is brief explanation of my case. I have a UItabBar in main view with two tab buttons. In one tab view, there is table view. If user taps on a row, a detail view is presented by using navigation controller. In the detail view, the tab bar is hidden, and a toolbar is showing at the bottom.
In order to bring tab bar back and hide the toolbar when the main view is brought back, I have to explicitly show tab bar and hide toolbar in the event of viewWillAppear:
class myMainViewController: UITableViewController {
private var tabBarHidden: Bool? = {
didSet {
self.tabBarController?.tabBar.isHidden = tabBarIsHidden ?? true
}
}
private var toolBarIsHidden: Bool? {
didSet {
let hidden = toolBarIsHidden ?? true
self.navigationController?.toolbar.isHidden = hidden
self.navigationController?.setToolbarHidden(hidden, animated: true)
}
}
...
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarIsHidden = false
self.toolBarIsHidden = true
}
...
}
I finally realize that the visibility of bar at the bottom is set in the event of viewWillAppear. At that time, the tableView or scroll view's content insets are set already based on no bar at the bottom. That's why my tableView is behind the bottom bar.
The solution I found is to reset content insets in the event of viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
// In the event of viewWillAppear, visibilities of tool bar and tab bar are set or changed,
// The following codes resets scroll view's content insets for tableview
let topInset = self.navigationController!.navigationBar.frame.origin.y +
self.navigationController!.navigationBar.frame.height
let adjustForTabbarInsets: UIEdgeInsets = UIEdgeInsetsMake(
topInset, 0,
self.tabBarController!.tabBar.frame.height, 0)
self.tableView.contentInset = adjustForTabbarInsets
self.tableView.scrollIndicatorInsets = adjustForTabbarInsets
}
The best approch would be to Embed TabBarController to your ViewController (Editor -> Embed In -> TabBar Controller)and set the bottom of the tableview to be bottom of safe area of viewcontroller. The other ways wont be as perfect as this one.
You need to adjust the height of the table view. Just leave 49px at the bottom, as the tabbar height is 49 px. Adjust the height of table view so that it leaves 49px space below it.

Resources