How to swipe to view maintaining navigation bar items? - ios

I'm trying to implement the swipe to view functionality just like Twitter's Moments:
If you take a look at the top you'll see a horizontal slider with the views. My requirement is tad bit different... On top of that horizontal slider I need my regular navigation bar with each page's custom items.
The swipe to view I implemented with some minor errors. Please take a look at this video (this is what I've done so far):
http://sendvid.com/5j50p73z
As you can see the swipe views functionality is working but I'm still missing some important things, namely:
When swiping to the next view it is scrolling up (seen on video)
My views are all contained in navigation controllers with their respective navbar items. The way I see it I'd need something like PushViewController without the back button but that would work both directions... I'm kinda lost here If I add the navigation controller instead of the view it shows the bar, but below the current existing one, not substituting it.
Any ideas on how to solve these?
I'm developing using Xamarin.iOS, but if you can provide an example using swift I guess I could try to translate that :)
This is what I have so far:
var viewController = Storyboard.InstantiateViewController("detailClaimViewController") as DetailClaimViewController;
viewController.ClaimId = ClaimId;
AddChildViewController(viewController);
ScrollView.AddSubview(viewController.View);
viewController.DidMoveToParentViewController(this);
var sumsInsController = Storyboard.InstantiateViewController("sumsInsuredListViewController")
as SumsInsuredListViewController;
var sumsInsuredFrame = sumsInsController.View.Frame;
sumsInsuredFrame.X = View.Frame.Size.Width;
sumsInsController.View.Frame = sumsInsuredFrame;
sumsInsController.ClaimId = ClaimId;
sumsInsController.Title = $"Sums insured for {Title}";
AddChildViewController(sumsInsController);
ScrollView.AddSubview(sumsInsController.View);
sumsInsController.DidMoveToParentViewController(this);
ScrollView.ContentSize = new CGSize(View.Frame.Size.Width * 2, View.Frame.Size.Height - 49);
This is the code I "need" to work, because it loads the navigation bar and that ViewController's custom navbar items (as I said this one "works" but it shows the correct navigation bar below the standard one, it doesn't substitute it):
var viewController = Storyboard.InstantiateViewController("detailClaimViewController") as DetailClaimViewController;
viewController.ClaimId = ClaimId;
AddChildViewController(viewController);
ScrollView.AddSubview(viewController.View);
viewController.DidMoveToParentViewController(this);
var navController = new UINavigationController();
var sumsInsController = Storyboard.InstantiateViewController("sumsInsuredListViewController")
as SumsInsuredListViewController;
var sumsInsuredFrame = sumsInsController.View.Frame;
sumsInsuredFrame.X = View.Frame.Size.Width;
sumsInsController.View.Frame = sumsInsuredFrame;
sumsInsController.ClaimId = ClaimId;
sumsInsController.Title = $"Sums insured for {Title}";
navController.AddChildViewController(sumsInsController);
AddChildViewController(navController);
ScrollView.AddSubview(navController.View);
navController.DidMoveToParentViewController(this);
ScrollView.ContentSize = new CGSize(View.Frame.Size.Width * 2, View.Frame.Size.Height - 49);
Thank you!

Navigation items are defined per contained view controller within a UINavigationController. You can work around that in different ways, but my recommendation would be to use a custom UIViewController that acts as the container for the other UIViewControllers you want to present.
For example, you could build a custom UIViewController that contains your custom navigation bar and a view for the content itself, to act as a custom UINavigationController. With custom transitions, you can mimic the behaviour of the original class.
Please refer to this document for a better explanation on container view controllers.

Related

Replacing edgesForExtendedLayout with safeAreaInsets or LayoutGuide **WITHOUT** interface builder

Preface: I'm doing everything programmatically. 0 interface builder.
Previously, when pushing a VC onto a navVC stack, the pushed VC would have the upper edge occluded by the navbar. I want it so the top edge of the pushed VC is under the nav bar. I found that "edgesForExtendedLayout" did what I wanted to. Except Apple says:
"Instead of this property, use the safe area of your view to determine which parts of your interface are occluded by other content. For more information, see the safeAreaLayoutGuide and safeAreaInsets properties of UIView."
I have no idea how to translate the following code:
if let nextVC = getNextVC() {
nextVC.edgesForExtendedLayout = .init(rawValue: 0)
pushViewController(nextVC, animated: true)
}
To use safeAreaLayoutGuide or safeAreaInsets. I can't use constraints because the pushed view is not in the view hierarchy yet?
I've attached some images to give an idea of what I'm dealing with.
Green = border of UINavigation controller
Yellow = border of 'nextVC'
Red = border of table inside 'nextVC'
With edgesForExtendedLayout set to "none" - right, but changing value Apple says not to change:
With edgesForExtendedLayout set to "all" - wrong, but default value Apple says to keep:

Search bar as header in tableview - appear and disappear

I need to put a search bar at the top of my tableview. I am making a network call and when the results are greater than 100 I want to search bar to appear and when they are less than 100 I don't want to search bar to appear. The tableview is on the right side of the VC and does not take up the whole view controller. I want the search bar to be at the top of the table view as a header.
I cannot use a search controller because in iOS 11, using a search controller makes the search bar pop to the top of the VC when it is active.
I tried to set the tableviewheader to nil to get it to disappear. But I can't get it back obviously because I made the header nil.
self.rightDetailFilterTableView.tableHeaderView = nil
self.rightDetailFilterTableView.sectionHeaderHeight = 0
I have put the search bar into the storyboard as seen in the image below. Is this the right way to add the search bar as a header?
What is the best way to get it to appear and disappear in the tableview? I have tried a bunch of different methods. They all either leave a blank header or do something else that causes problems. I also tried using the header delegate methods but that still did not work.
I am not using a tableview controller, I am using a normal VC. I am also not using a search bar controller because of issues it causes in iOS 11.
Here's what I've done in one of my recent project. First, laid out my views like so:
That is, the Search Bar was added to the parent view rather than the table view. This allows me to hide/show it as needed.
Next, I've defined two optional layout constraint, one ensuring that the tableview is aligned to the top of the safe area, priority 750; the other aligning the top of the search bar to the top of the safe area; priority lower than 750 to hide it below the nav bar or priority higher than 750 to reveal it and push the table view down.
In code, I created a #IBOutlet to the layout constraint for the search bar to the top of the safe area, and I change its priority as needed:
#IBAction
func toggleSearchBar(_ sender: Any?) {
if searchBarVisibleLayoutConstraint.priority.rawValue > 750.0 {
searchBarVisibleLayoutConstraint.priority = UILayoutPriority(rawValue: 1.0)
searchBar?.endEditing(true)
} else {
searchBarVisibleLayoutConstraint.priority = UILayoutPriority(rawValue: 999.0)
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
In my case, the navigation bar is opaque and the search bar is not visible behind it. Your case may be different so you may also want to either clip the parent view or alpha fade the search bar when it is not visible.
Good luck!
Please check :
Created IBOutlet for my SearchBar.
#IBOutlet weak var testbar: UISearchBar!
And in my viewDidLoad :
override func viewDidLoad() {
var contentOffset = tableView.contentOffset
let showSearchBar = (results.count > 100)
self.tableView.tableHeaderView?.isHidden = !(showSearchBar)
if showSearchBar {
contentOffset.y -= testbar.frame.size.height
} else {
contentOffset.y += testbar.frame.size.height
}
tableView.contentOffset = contentOffset
}
Here is my tableview storyboard

Custom title view as large title in iOS 11 new navigation bar

I am using a button as a title view for my UITableViewController which opens a dropdown list of categories. Selecting a category filters content of the table view by the selected category.
The button shows the name of the selected category plus a small arrow, similar to how iBooks used to look (or maybe still looks? I haven't used it in a while). I would therefore like it to have the same behaviour as a standard title and have it be large at first and collapse when the table view is scrolled.
Is there a way to do this?
Thanks
It seems because of the new large titles, IOS11 requires the constraints on the custom view in the navigationItem.titleView to be set.
Do this for example:
customView.widthAnchor.constraint(equalToConstant: 200).isActive = true
customView.heightAnchor.constraint(equalToConstant: 44).isActive = true
self.navigationItem.titleView = customView
Note this must be done for both width and height.
It should work. No need to add a button, at least in my case...
This was suggested by Apple to ensure that you don't have zero-size custom views. See slide 33 in https://developer.apple.com/videos/play/wwdc2017/204/
Looks like touches are broken for navigationItem.titleView. Gestures, tap events and buttons - nothing works
Seems like a bug in iOS 11: https://forums.developer.apple.com/thread/82466
I provisionally implemented this workaround:
private lazy var navBarActionButtonIOS11: UIButton = {
button.addTarget(self.navTitleView, action: #selector(self.navTitleView.didTapView), for: .touchUpInside)
return button
}()
[...]
navigationItem.titleView = navTitleView
if #available(iOS 11.0, *), let navBar = navigationController?.navigationBar {
navBarActionButtonIOS11.removeFromSuperview()
navBar.addSubview(navBarActionButtonIOS11)
navBarActionButtonIOS11.center.x = navBar.center.x
}
Another solution could be to just assign a UIButton to navigationItem.titleView directly.
I hope Apple fixes this soon!
Well, I had same problem. I have UIButtons in UINavigationItem.titleView and those were not reacting to touches at all. Problem is that the view where those buttons are where of size (0,0) because of auto layout. So to fix this problem you need to add additional view into your custom view, lets call it "contentView" and put all your controls inside that contentView. Also, contentView must have defined size with constraints. Quick test is to add width and height constraint to contentView. And all works again.
Hope that this helps someone.

UISplitViewController Master Content Width

I have a UISplitViewController in my application (MvvmCross / Xamarin iOS) and for some reason I cannot get the content to respect the dimensions of the available view areas.
In the situation shown in the screenshot the master view is hosting a UIViewController with a TableView inside. All the layouts are done with constraints and work fine on their own when running in an iPhone emulator.
As soon as I switching to running on an iPad some custom code I have in my presenter shows this same view in the master panel of a UISplitViewController but in this situation the constraints seem to be ignored and I end up with a view that looks like this:
As you can see the right hand side of the table cell is now way off the viewable area of the master panel of the UISplitViewController.
Both the UITableView and the UITableCell both use View.Frame as their initial size (I've tried View.Bounds as well).
How can I get the cells and / or table to respect the bounds of the UISplitViewController available space?
Thanks to Cheesebarons question I found my solution (cause).
I have a set of methods in a helper class that I use to generate my "default" UIViews.
One of these methods creates my default UITableView:
public static UITableView CreateDefaultTableView(CGRect rect, UITableViewStyle style)
{
var tv = new UITableView(rect, style)
{
AutoresizingMask = UIViewAutoresizing.FlexibleHeight,
SeparatorStyle = UITableViewCellSeparatorStyle.SingleLine,
SeparatorColor = IosConstants.DefaultTableSeparatorColor,
BackgroundColor = IosConstants.DefaultViewBackgroundColor
};
return tv;
}
Changing:
AutoresizingMask = UIViewAutoresizing.FlexibleHeight,
To:
AutoresizingMask = UIViewAutoresizing.All,
Schoolboy error!

IOS:UITabbar item click again and again it is reducing the UITabbar button item size in IOS 7

I'm took the Tabbar viewcontroller in this ,I added the 5 item and .I given the image insects is (24,0,0,6).
All button images are added in xib [under the Bar item -->image]Please help.
Thanks.
Adding to a similar answer here:
iOS Tab Bar icons keep getting larger
Not sure if this is an iOS7 bug but I've noticed that image insets need to be balanced.
You have specified insets for top and right but:
if you set a top inset, in order to balance it, you need to set the negative of it to the bottom inset
if you set a right inset, in order to balance it, you need to set the negative of it to the left inset
So, instead of having image insets like (24,0,0,6), use balanced image insets such as UIEdgeInsetsMake(24,-6,-24,6)
Doing so should protect your tabBarItem image from getting whacked on every tap.
If this doesn't suit your requirements, then redesign your tabBarItem image so you can have balance insets or... no insets at all.
Here's the workaround for a bug I've encountered with UITabBarController's UITabBar. If I tap a UITabBarItem once after it's selected, the icon shrinks. What I'd like to do is disable touches. UITabBarItem only has a setting for isEnabled, which grays it out if I set it to false...not what I was looking for.
I used a derivative of this answer to figure it out. With a UITabBarController with 3 tabs, printing tabBarController.subviews, I saw 3 UITabBarButtons and a UIBarBackground. The origin of UIBarBackground's frame was always (0, 0), putting it at the front of the sorted array, so I really don't need to know what the subview is, just "where it is" and whether it will always be there. The UIBarBackground is always going to be at the front of an array of tabBarController.subviews sorted by frame.minX, so I just need to remove it from the front.
Solution
Here's what the extension looks like:
extension UITabBarController {
var buttonViews: [UIView] {
var tabBarButtons = tabBar.subviews.sorted(by: {$0.frame.minX < $1.frame.minX})
tabBarButtons.removeFirst()
return tabBarButtons
}
}
I also created a struct in my Constants file, so I don't have to remember tab names:
struct TabBarItem {
static let firstTab = 0
static let secondTab = 1
static let thirdTab = 2
}
...and finally, where to use it:
In viewDidAppear (NOT viewDidLoad), add the following line to disable the UITabBarItem that you don't want to disable, but not gray out:
tabBarController?.buttonViews[TabBarItem.firstTab].isUserInteractionEnabled = false
In viewWillDisappear, re-enable the tab, as follows:
tabBarController?.buttonViews[TabBarItem.firstTab].isUserInteractionEnabled = true

Resources