I need to adjust the vertical position of my UIBarButtonItem in UINavigationBar.I think there is a way to do this. To add a UIView to UIBarButtonItem and add a UIButton to the UIView. It's ok if you have only one or two navigation bar. But I think it's a bit too troublesome if you have dozens of UINavigationBar especially inside a storyboard. So I did some researches and found a easy solution for this. That is to use category. Here is my source code:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
-(void)viewWillAppear:(BOOL)animated
{
[self show:NO];
}
// do your override
-(void)viewWillLayoutSubviews
{
[self changeTheButtonPosition:self.navigationItem.leftBarButtonItem];
[self changeTheButtonPosition:self.navigationItem.rightBarButtonItem];
}
-(void)viewDidAppear:(BOOL)animated
{
[self show:YES];
}
#pragma clang diagnostic pop
-(void)show:(BOOL)show
{
self.navigationItem.leftBarButtonItem.customView.hidden = !show;
self.navigationItem.rightBarButtonItem.customView.hidden = !show;
}
- (void)changeTheButtonPosition:(UIBarButtonItem *)barButtonItem
{
if ( barButtonItem )
{
UIView* customView = barButtonItem.customView;
// NSLog(#"custom view frame = %#",NSStringFromCGRect(customView.frame));
CGRect frame = customView.frame;
CGFloat y = SYSTEM_VERSION_LESS_THAN(#"7") ? 10.0f : 5.0f;
customView.frame = CGRectMake(frame.origin.x, y, frame.size.width, frame.size.height);
}
}
It works perfectly in iOS 7 and in the first view controller in iOS 6. But it doesn't work for the pushed UIViewController in iOS 6. I can't find any reason for that. Can anybody advise? What's wrong in my code in iOS 6?
You should be able to achieve this using the UIAppearance proxy methods introduced in iOS5. In particular:
- (void)setBackgroundVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics
- (void)setTitlePositionAdjustment:(UIOffset)adjustment forBarMetrics:(UIBarMetrics)barMetrics
Check the UIBarButtonItem docs for more info.
Related
I am developing an app for iPad only. In which in for one functionality i want to display FullCustom Popover.
For that my code is as below:-
DuplicateViewController *viewControllerForPopover =
[self.storyboard instantiateViewControllerWithIdentifier:#"DuplicatePopoverVC"];
viewControllerForPopover.arr_studentDetail = self.arrStudentDetail;
viewControllerForPopover.dictSelectedProg = dictSelectedProgram;
self.popover = [[UIPopoverController alloc]
initWithContentViewController:viewControllerForPopover];
[self.popover setPopoverContentSize:CGSizeMake(self.view.frame.size.width, self.view.frame.size.height)];
viewControllerForPopover.modalPresentationStyle = UIModalPresentationFullScreen;
[self.popover setBackgroundColor:[[UIColor darkGrayColor] colorWithAlphaComponent:0.4]];
[self.popover presentPopoverFromRect:self.view.bounds inView:self.view permittedArrowDirections:0 animated:YES];
I set popover size and tried with other option but can't make it full screen.
ViewDidLoad -> DuplicateViewController
[view_main.layer setBorderWidth:5.0f];
[view_main.layer setCornerRadius:25.0f];
[view_main.layer setBorderColor:[UIColor colorWithRed:(29.0f/255.0f) green:134.0f/255.0f blue:140.0f/255.0f alpha:1.0f].CGColor];
But while running App, It display as below:-
Please help me to display full screen Popover. Thank you so much in advance.
create two classes
the first one should inherit UIPopoverController
the second one should inherit UIPopoverBackgroundView
for example:
CustomUIPopoverController.h
#interface CustomUIPopoverController : UIPopoverController
CustomUIPopoverController.m
#implementation CustomUIPopoverController
- (id)initWithContentViewController:(UIViewController *)viewController {
self = [super initWithContentViewController:viewController];
if (self) {
self.popoverBackgroundViewClass = [CustomUIClearPopoverBackgroundView class];
[self setPopoverContentSize:viewController.view.frame.size];
}
return self;
}
#end
CustomUIClearPopoverBackgroundView.h
#interface CustomUIClearPopoverBackgroundView : UIPopoverBackgroundView
CustomUIClearPopoverBackgroundView.m
#import "CustomUIClearPopoverBackgroundView.h"
#implementation CustomUIClearPopoverBackgroundView
#pragma mark - no arrow
+ (CGFloat)arrowHeight {
return 0;
}
+ (CGFloat)arrowBase {
return 0;
}
- (CGFloat)arrowOffset {
return 0;
}
- (void)setArrowOffset:(CGFloat)arrowOffset {
}
- (UIPopoverArrowDirection)arrowDirection {
return 0;
}
- (void)setArrowDirection:(UIPopoverArrowDirection)arrowDirection {
}
#pragma mark - no margins
+ (UIEdgeInsets)contentViewInsets {
return UIEdgeInsetsZero;
}
#pragma mark - fully transparent (default is 0.15)
+(BOOL)wantsDefaultContentAppearance {
return NO;
}
- (void)willMoveToWindow:(UIWindow *)newWindow {
[super willMoveToWindow:newWindow];
// hide shadow image view
[self.superview.subviews[0] setHidden:YES];
}
#end
I used this but I don't like it very much.
the second option is to create a container view and hide/show it as you like.
this way it's a pure view controller and you don't need to mess with popup stuff
to create the transparency, make your container view background = clear, the view controller view background color = clear, and add a semi-transparent button in the size of the view controller for the semi-transparent black background. on click of the button close the window (= same effect as tapping the popup background)
I am implementing a view that is in some way similar to what happens in Messages app, so there is a view with UITextView attached to the bottom of the screen and there is also UITableView showing the main content. When it is tapped it slides up with the keyboard and when keyboard is dismissed it slides back to the bottom of the screen.
That part I have and it is working perfectly - I just subscribed to keyboard notifications - will hide and wil show.
The problem is that I have set keyboard dismiss mode on UITableView to interactive and I cannot capture changes to keyboard when it is panning.
The second problem is that this bar with uitextview is covering some part of uitableview. How to fix this? I still want the uitableview to be "under" this bar just like in messages app.
I am using AutoLayout in all places.
Any help will be appreciated!
============
EDIT1:
Here is some code:
View Hierarchy is as follows:
View
- UITableView (this one will contain "messages")
- UIView (this one will slide)
UITableView is has constraints to top, left, right and bottom of parent view so it fills whole screen.
UIView has constraints to left, right and bottom of parent view so it is glued to the bottom - I moved it by adjusting constant on constraint.
In ViewWillAppear method:
NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.DidShowNotification, OnKeyboardDidShowNotification);
NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillChangeFrameNotification, OnKeyboardDidShowNotification);
NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillHideNotification, OnKeyboardWillHideNotification);
And here are methods:
void OnKeyboardDidShowNotification (NSNotification notification)
{
AdjustViewToKeyboard (Ui.KeyboardHeightFromNotification (notification), notification);
}
void OnKeyboardWillHideNotification (NSNotification notification)
{
AdjustViewToKeyboard (0.0f, notification);
}
void AdjustViewToKeyboard (float offset, NSNotification notification = null)
{
commentEditViewBottomConstraint.Constant = -offset;
if (notification != null) {
UIView.BeginAnimations (null, IntPtr.Zero);
UIView.SetAnimationDuration (Ui.KeyboardAnimationDurationFromNotification (notification));
UIView.SetAnimationCurve ((UIViewAnimationCurve)Ui.KeyboardAnimationCurveFromNotification (notification));
UIView.SetAnimationBeginsFromCurrentState (true);
}
View.LayoutIfNeeded ();
commentEditView.LayoutIfNeeded ();
var insets = commentsListView.ContentInset;
insets.Bottom = offset;
commentsListView.ContentInset = insets;
if (notification != null) {
UIView.CommitAnimations ();
}
}
I'd recommend you to override -inputAccessoryView property of your view controller and have your editable UITextView as its subview.
Also, don't forget to override -canBecomeFirstResponder method to return YES.
- (BOOL)canBecomeFirstResponder
{
if (!RUNNING_ON_IOS7 && !RUNNING_ON_IPAD)
{
//Workaround for iOS6-specific bug
return !(self.viewDisappearing) && (!self.viewAppearing);
}
return !(self.viewDisappearing);
}
With this approach system manages everything.
There are also some workarounds you must know about: for UISplitViewController (UISplitViewController detail-only inputAccessoryView), for deallocation bugs (UIViewController with inputAccessoryView is not deallocated) and so on.
This solution is based on a lot of different answers on SO. It have a lot of benefits:
Compose bar stays on bottom when keyboard is hidden
Compose bas follows keyboard while interactive gesture on UITableView
UITableViewCells are going from bottom to top, like in Messages app
Keyboard do not prevent to see all UITableViewCells
Should work for iOS6, iOS7 and iOS8
This code just works:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = // . . .
// . . .
cell.contentView.transform = CGAffineTransformMakeScale(1,-1);
cell.accessoryView.transform = CGAffineTransformMakeScale(1,-1);
return cell;
}
- (UIView *)inputAccessoryView {
return self.composeBar;
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.tableView.transform = CGAffineTransformMakeScale(1,-1);
// This code prevent bottom inset animation while appearing view
UIEdgeInsets newEdgeInsets = self.tableView.contentInset;
newEdgeInsets.top = CGRectGetMaxY(self.navigationController.navigationBar.frame);
newEdgeInsets.bottom = self.view.bounds.size.height - self.composeBar.frame.origin.y;
self.tableView.contentInset = newEdgeInsets;
self.tableView.scrollIndicatorInsets = newEdgeInsets;
self.tableView.contentOffset = CGPointMake(0, -newEdgeInsets.bottom);
// This code need to be done if you added compose bar via IB
self.composeBar.delegate = self;
[self.composeBar removeFromSuperview];
[[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillChangeFrameNotification object:nil queue:nil usingBlock:^(NSNotification *note)
{
NSNumber *duration = note.userInfo[UIKeyboardAnimationDurationUserInfoKey];
NSNumber *options = note.userInfo[UIKeyboardAnimationCurveUserInfoKey];
CGRect beginFrame = [note.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGRect endFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIEdgeInsets newEdgeInsets = self.tableView.contentInset;
newEdgeInsets.bottom = self.view.bounds.size.height - endFrame.origin.y;
CGPoint newContentOffset = self.tableView.contentOffset;
newContentOffset.y += endFrame.origin.y - beginFrame.origin.y;
[UIView animateWithDuration:duration.doubleValue
delay:0.0
options:options.integerValue << 16
animations:^{
self.tableView.contentInset = newEdgeInsets;
self.tableView.scrollIndicatorInsets = newEdgeInsets;
self.tableView.contentOffset = newContentOffset;
} completion:^(BOOL finished) {
;
}];
}];
}
Use for example pod 'PHFComposeBarView' compose bar:
#property (nonatomic, strong) IBOutlet PHFComposeBarView *composeBar;
And use this class for your table view:
#interface InverseTableView : UITableView
#end
#implementation InverseTableView
void swapCGFLoat(CGFloat *a, CGFloat *b) {
CGFloat tmp = *a;
*a = *b;
*b = tmp;
}
- (UIEdgeInsets)contentInset {
UIEdgeInsets insets = [super contentInset];
swapCGFLoat(&insets.top, &insets.bottom);
return insets;
}
- (void)setContentInset:(UIEdgeInsets)contentInset {
swapCGFLoat(&contentInset.top, &contentInset.bottom);
[super setContentInset:contentInset];
}
#end
If you would like keyboard to disappear by tapping on message:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.composeBar.textView resignFirstResponder];
}
Do not call this, this will hide composeBar at all:
[self resignFirstResponder];
UPDATE 2:
NEW SOLUTION for keyboard tracking works much better:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Compose view height growing tracking
[self.composeBar addObserver:self forKeyPath:#"frame" options:0 context:nil];
// iOS 7 keyboard tracking
[self.composeBar.superview addObserver:self forKeyPath:#"center" options:0 context:nil];
// iOS 8 keyboard tracking
[self.composeBar.superview addObserver:self forKeyPath:#"frame" options:0 context:nil];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.composeBar removeObserver:self forKeyPath:#"frame"];
[self.composeBar.superview removeObserver:self forKeyPath:#"center"];
[self.composeBar.superview removeObserver:self forKeyPath:#"frame"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == self.composeBar.superview || object == self.composeBar)
{
// Get all values
CGPoint newContentOffset = self.tableView.contentOffset;
UIEdgeInsets newEdgeInsets = self.tableView.contentInset;
UIEdgeInsets newScrollIndicartorInsets = self.tableView.scrollIndicatorInsets;
// Update values
CGFloat bottomInset = self.view.bounds.size.height - [self.composeBar convertPoint:CGPointZero toView:self.view].y;
CGFloat diff = newEdgeInsets.bottom - (bottomInset + 7);
newContentOffset.y += diff;
newEdgeInsets.bottom = bottomInset + 7;
newScrollIndicartorInsets.bottom = bottomInset;
// Set all values
if (diff < 0 || diff > 40)
self.tableView.contentOffset = CGPointMake(0, newContentOffset.y);
self.tableView.contentInset = newEdgeInsets;
self.tableView.scrollIndicatorInsets = newEdgeInsets;
}
}
OK, the interactive keyboard dismissal will send a notification with name UIKeyboardDidChangeFrameNotification.
This can be used to move the text view while the keyboard is being dismissed interactively.
You are already using this but you are sending it to the OnKeyboardDidShow method.
You need a third method called something like keyboardFramedDidChange. This works for the hide and the show.
For the second problem, you should have your vertical constraints like this...
|[theTableView][theTextView (==44)]|
This will tie the bottom of the tableview to the top of the text view.
This doesn't change how any of the animation works it will just make sure that the table view will show all of its contents whether the keyboard is visible or not.
Don't update the content insets of the table view. Use the constraints to make sure the frames do not overlap.
P.S. sort out your naming conventions. Method names start with a lowercase letter.
P.P.S. use block based animations.
I'd try to use an empty, zero-height inputAccessoryView. The trick is to glue your text field's bottom to it when the keyboard appears, so that they'd move together. When the keyboard is gone, you can destroy that constraint and stick to the bottom of the screen once again.
I made an open source lib for exactly this purpose. It works on iOS 7 and 8 and is set up to work as a cocoapod as well.
https://github.com/oseparovic/MessageComposerView
Here's a sample of what it looks like:
You can use a very basic init function as shown below to create it with screen width and default height e.g.:
self.messageComposerView = [[MessageComposerView alloc] init];
self.messageComposerView.delegate = self;
[self.view addSubview:self.messageComposerView];
There are several other initializers that are also available to allow you to customize the frame, keyboard offset and textview max height as well as some delegates to hook into frame changes and button clicks. See readme for more!
Basically what I'm trying to achieve is to have my scope bar to never disappear.
Environment : IOS 7, storyboard, inside a view controller I have a "search bar and search display controller" and a separate tableview (the searchbar is not inside the table)
Inside the view controller.h
#property (nonatomic, strong) IBOutlet UISearchBar *candySearchBar;
Inside the view controller.m
#synthesize candySearchBar;
What I tried : inside a custom search bar class
- (void) setShowsScopeBar:(BOOL) showsScopeBar
{
if ([self showsScopeBar] != showsScopeBar) {
[super invalidateIntrinsicContentSize];
}
[super setShowsScopeBar:showsScopeBar];
[super setShowsScopeBar: YES]; // always show!
NSLog(#"setShowsScopeBar searchbar");
NSLog(#"%hhd", showsScopeBar);
}
and
searchBarDidEndEditing
Same thing in the view controller, but then
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[candySearchBar setShowsScopeBar:YES];
[candySearchBar sizeToFit];
}
I hope my question is clear, I tried many solutions posted all over the internet, most of them talk about the setshowsscopebar, but it doesn't seem to work. The output of the log in setshowscopebar is 1, but the scopebar is still not shown.
I still consider myself to be new to the code, the fault can still be a newbie mistake.
edit : another piece of code in the view controller, as you can see i'm searching blind:
-(void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller{
self.searchDisplayController.searchBar.showsCancelButton = YES;
self.searchDisplayController.searchBar.showsScopeBar = YES;
controller.searchBar.showsScopeBar = TRUE;
controller.searchBar.frame = CGRectMake(0, 149, 768, 88);
UIButton *cancelButton;
UIView *topView = self.searchDisplayController.searchBar.subviews[0];
for (UIView *subView in topView.subviews) {
if ([subView isKindOfClass:NSClassFromString(#"UINavigationButton")]) {
cancelButton = (UIButton*)subView;
}
}
if (cancelButton) {
//Set the new title of the cancel button
[cancelButton setTitle:#"Cancel" forState:UIControlStateNormal];
[cancelButton setEnabled:YES];
controller.searchBar.showsScopeBar = YES;
//candySearchBar.scopeButtonTitles = [NSArray arrayWithObjects:#"Flags", #"Listeners", #"Stations", nil];
}
NSLog(#"%#",NSStringFromCGRect(controller.searchBar.frame));
NSLog(#"%#",NSStringFromCGRect(controller.searchBar.bounds));
NSLog(#"%hhd#",controller.searchBar.hidden);
}
The code you tried will not work in iOS7 onward because apple has changed it behavior of UISearchBar to hide the scope when return to normal view. Add this method to your custom searchBar class.
-(void)layoutSubviews
{
[super layoutSubviews];
if([[UIDevice currentDevice].systemVersion floatValue]>=7.0) {
//Get search bar with scope bar to reappear after search keyboard is dismissed
[[[[self.subviews objectAtIndex:0] subviews] objectAtIndex:0] setHidden:NO];
[self setShowsScopeBar:YES];
}
}
Directly accessing object at index may crash the app in iOS6 because of difference in view hierarchy between iOS6 and iOS7, to avoid this, add this inside if condition only when its iOS7.
In addition this is also required in the custom search bar class
-(void) setShowsScopeBar:(BOOL)showsScopeBar {
[super setShowsScopeBar:YES]; //Initially make search bar appear with scope bar
}
I have the same issue. Perhaps it is something that has changed in iOS7 since showing the scope bar is supposed to be the default behaviour. You can verify this in the section "Creating an Optional Scope Bar to Filter Results" of the following tutorial:
http://www.raywenderlich.com/16873/how-to-add-search-into-a-table-view
Hopefully someone has a solution for this; otherwise we will have to look for a workaround.
initialize set scope bar NO
[self.searchBar setShowsScopeBar:NO];
[self.searchBar sizeToFit];
//default scope bar selection
self.searchBar.selectedScopeButtonIndex=3;
unselect/remove tick from scopeBar checkbox
It's possible (but hacky) to do this without a custom searchBar, in a pretty similar way to what CoolMonster suggests.
In your TableViewController, this will show the ScopeBar after a search ends:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
//Show the scopeBars
controller.searchBar.showsScopeBar = YES;
//Resize the searchBar to show ScopeBar
controller.searchBar.frame = CGRectMake(0, 0, 320, 88);
if([[UIDevice currentDevice].systemVersion floatValue]>=7.0) {
[[[[controller.searchBar.subviews objectAtIndex:0] subviews] objectAtIndex:0] setHidden:NO];
}
}
Then, since you probably want it to appear before you search, add this line to the TableViewController's viewDidLoad:
[self searchDisplayControllerDidEndSearch:self.searchDisplayController];
For the record, after getting this to work, I ended up using a separate segmented control instead of the approach above for several reasons, not least of which was that touching the ScopeBar of a SearchBar, once you get it to display, launches the search display tableView, which makes of sense if you're using it the recommended way. However, since I wanted the ScopeBar to work without launching the search tableview, for me it made more sense just to use my own segmented control and add it to my tableHeaderView under the searchBar.
Since the iOS7 upgrade, I have a weird behaviour of the UIImagePickerController. In this application I am using the UIImagePickerController with a cameraOverlayView.
In iOS6 I called the UIImagePickerController using the following code:
_picker = [[UIImagePickerController alloc] init];
if ([UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]) {
_picker.sourceType = UIImagePickerControllerSourceTypeCamera;
_picker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;
_picker.cameraDevice = UIImagePickerControllerCameraDeviceRear;
_picker.showsCameraControls = NO;
_picker.navigationBarHidden = NO;
_picker.toolbarHidden = YES;
_picker.wantsFullScreenLayout = YES;
_overlayViewController = [[OverlayViewController alloc] init];
_overlayViewController.picker = _picker;
_overlayViewController.frameSize = self.frameSize;
_overlayViewController.delegate = self;
_picker.cameraOverlayView = _overlayViewController.view;
}
else {
_picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
}
_picker.delegate = self;
Where the OverlayViewController is an UIViewController, with a transparent background which draws some custom controls on screen.
But now in iOS 7 the camera is drawn through the statusbar and a black bar appears beneath the live camera view.
I can solve this by applying a CGAffineTransformMakeTranslation to the cameraViewTransform property of the UIImagePickerController, but why is this like this?
In iOS 7, by default UIViewController views take up the entire screen area including the status bar.
wantsFullScreenLayout
is deprecated and ignored. In some cases, this fix works (in the view controller class):
if ([self respondsToSelector:#selector(setEdgesForExtendedLayout:)]) {
[self setEdgesForExtendedLayout:UIRectEdgeNone];
}
In other cases, it's a bit more complicated. It's late here, so see how you go with it. Helpful things to note - in a UIViewController, the following code will give the correct statusbar height on both iOS 6 and iOS 7, should it come to having to align things using CGRect math:
if (UIDeviceOrientationIsLandscape(self.interfaceOrientation)) {
statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.width;
} else {
statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
}
And then don't forget that in Interface Builder, there are the new "iOS 6 delta" adjustments that allow you to design for iOS 7 and then use offsets to correct for iOS 6.
Anyhow, let me know how you go.
My understanding of the issue, based on a few other SO threads and such, is that UIImagePickerController does not do what we'd expect in terms of managing the status bar via [UIViewController -prefersStatusBarHidden].
This means you either have to disable view controller status bar management entirely, via plist, or figure out a way to get UIImagePickerController to do what we want. On the assumption that you're not looking for the former, I can say I've had success in the latter by putting the picker in a wrapper controller that does what I want (but fall back to your previous code if you still need to detect/support iOS6):
#interface PickerContainer : UIViewController
#property ( nonatomic, weak ) UIImagePickerController* picker;
#end
#implementation PickerContainer
- (void) setPicker: (UIImagePickerController*) picker
{
[self addChildViewController: picker];
[picker didMoveToParentViewController: self];
self->_picker = picker;
}
- (void) viewDidLoad
{
[super viewDidLoad];
self.picker.view.frame = self.view.bounds;
[self.view addSubview: self.picker.view];
}
// Will have no effect in ios6 -- see [-init] for that option
- (BOOL) prefersStatusBarHidden { return YES; }
- (id) init
{
if ( ! ( self = [super init] ) ) return nil;
if ( detectThatThisIsIos6() ) self.wantsFullScreenLayout = YES;
return self;
}
#end
This will work for you, scaled camera, you will have a black bar at the bottom but it will get overlayed by tool bar
https://stackoverflow.com/a/15803947
The iPad programming guide says that the splitView's left pane is fixed to 320 points. But 320 pixels for my master view controller is too much. I would like to reduce it and give more space to detail view controller. Is it possible by anyway?
Link to the document which speaks about fixed width.
If you subclass UISplitViewController, you can implement -viewDidLayoutSubviews and adjust the width there. This is clean, no hacks or private APIs, and works even with rotation.
- (void)viewDidLayoutSubviews
{
const CGFloat kMasterViewWidth = 240.0;
UIViewController *masterViewController = [self.viewControllers objectAtIndex:0];
UIViewController *detailViewController = [self.viewControllers objectAtIndex:1];
if (detailViewController.view.frame.origin.x > 0.0) {
// Adjust the width of the master view
CGRect masterViewFrame = masterViewController.view.frame;
CGFloat deltaX = masterViewFrame.size.width - kMasterViewWidth;
masterViewFrame.size.width -= deltaX;
masterViewController.view.frame = masterViewFrame;
// Adjust the width of the detail view
CGRect detailViewFrame = detailViewController.view.frame;
detailViewFrame.origin.x -= deltaX;
detailViewFrame.size.width += deltaX;
detailViewController.view.frame = detailViewFrame;
[masterViewController.view setNeedsLayout];
[detailViewController.view setNeedsLayout];
}
}
In IOS 8.0 you can easily do this by doing the following:
1. In your MasterSplitViewController.h add
#property(nonatomic, assign) CGFloat maximumPrimaryColumnWidth NS_AVAILABLE_IOS(8_0);
2. In your MasterSplitViewController.m viewDidLoad method add
self.maximumPrimaryColumnWidth = 100;
self.splitViewController.maximumPrimaryColumnWidth = self.maximumPrimaryColumnWidth;
This is a really good, simple and easy feature of IOS 8.
this code is work for me
[splitViewController setValue:[NSNumber numberWithFloat:200.0] forKey:#"_masterColumnWidth"];
No.
There are two private properties
#property(access,nonatomic) CGFloat masterColumnWidth;
#property(access,nonatomic) CGFloat leftColumnWidth; // both are the same!
but being private mean they can't be used for AppStore apps.
iOS 8 introduced a new property:
// An animatable property that can be used to adjust the maximum absolute width of the primary view controller in the split view controller.
#property(nonatomic, assign) CGFloat maximumPrimaryColumnWidth NS_AVAILABLE_IOS(8_0); // default: UISplitViewControllerAutomaticDimension
Use this property to adjust your master viewcontroller to your desired width.
Here is how I did this in iOS8 with Swift.
class MainSplitViewController: UISplitViewController {
override func viewDidLoad() {
self.preferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible
self.maximumPrimaryColumnWidth = 100 // specify your width here
}
}
If you need to change the width dynamically from within your master/detail view in the split view, then do something like this:
var splitViewController = self.splitViewController as MainSplitViewController
splitViewController.maximumPrimaryColumnWidth = 400
The storyboard way would be this one, mentioned by #Tim:
Furthermore, if you want the Master view to always take up a certain percentage of the screen then you can use the Key Path = "preferredPrimaryColumnWidthFraction" instead and set the value to 0.2 (for 20% screen size).
Please note that the "maximumPrimaryColumnWidth" is set to 320, so if you try the screen percent value of 0.5 (50%) it won't go above 320. You can add a key path for maximumPrimaryColumnWidth if you need to override this.
None of the answers worked for me on iOS7, so I did some of my own research and created a working solution. This will involve subclassing UISplitViewController for the full functionality.
I will present the answer as if we just created a new project for iPad with all device orientations and have set the custom UISplitViewController as the main view controller.
Create your custom UISplitViewController. In this example mine is called MySplitViewController. All code will be based in MySplitViewController.m.
We're going to need to access a method from the UISplitViewControllerDelegate so add that and set the delegate. We'll also setup a delegate forwarder incase you need to call the delegate methods from another class.
#interface MySplitViewController () <UISplitViewControllerDelegate>
#property (nonatomic, weak) id<UISplitViewControllerDelegate> realDelegate;
#end
#implementation MySplitViewController
- (instancetype)init {
self = [super init];
if (self) {
self.delegate = self;
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
self.delegate = self;
}
return self;
}
- (void)setDelegate:(id<UISplitViewControllerDelegate>)delegate {
[super setDelegate:nil];
self.realDelegate = (delegate != self) ? delegate : nil;
[super setDelegate:delegate ? self : nil];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
id delegate = self.realDelegate;
return [super respondsToSelector:aSelector] || [delegate respondsToSelector:aSelector];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
id delegate = self.realDelegate;
return [delegate respondsToSelector:aSelector] ? delegate : [super forwardingTargetForSelector:aSelector];
}
Setup the master and detail view controllers.
- (void)viewDidLoad {
[super viewDidLoad];
UIViewController* masterViewController = [[UIViewController alloc] init];
masterViewController.view.backgroundColor = [UIColor yellowColor];
UIViewController* detailViewController = [[UIViewController alloc] init];
detailViewController.view.backgroundColor = [UIColor cyanColor];
self.viewControllers = #[masterViewController, detailViewController];
}
Lets add our desired width to a method for easy reference.
- (CGFloat)desiredWidth {
return 200.0f;
}
We'll manipulate the master view controller before presenting it.
- (void)splitViewController:(UISplitViewController *)svc popoverController:(UIPopoverController *)pc willPresentViewController:(UIViewController *)aViewController {
id realDelegate = self.realDelegate;
if ([realDelegate respondsToSelector:#selector(splitViewController:popoverController:willPresentViewController:)]) {
[realDelegate splitViewController:svc popoverController:pc willPresentViewController:aViewController];
}
CGRect rect = aViewController.view.frame;
rect.size.width = [self desiredWidth];
aViewController.view.frame = rect;
aViewController.view.superview.clipsToBounds = NO;
}
However, now we're left with a display like this.
So were going to override a private method. Yes a private method, it will still be acceptable in the App Store since its not an underscore private method.
- (CGFloat)leftColumnWidth {
return [self desiredWidth];
}
This deals with portrait mode. So a similar thing for -splitViewController:willShowViewController:invalidatingBarButtonItem: and you should be set for landscape.
However none of this will be needed in iOS8. You'll be able to simply call a min and max width property!
use the following code before assigning to the rootviewcontroller. It works for me with ios7
[self.splitViewController setValue:[NSNumber numberWithFloat:256.0] forKey:#"_masterColumnWidth"];
self.window.rootViewController = self.splitViewController;
Since no one mentioned that this can be done from IB, I want to add this answer. Apparently, you can set "User Defined Runtime Attributes" for the UISplitViewContorller with following details:
Key Path:masterColumnWidth
Type: Number
Value: 250
In my case, I had to set both maximum and minimum to make this work
mySplitViewController.preferredDisplayMode = .allVisible;
mySplitViewController.maximumPrimaryColumnWidth = UIScreen.main.bounds.width/2;
mySplitViewController.minimumPrimaryColumnWidth = UIScreen.main.bounds.width/2;
You can use GSSplitViewController. This one will work on iOS 7 and 8
splitView = [[GSSplitViewController alloc] init];
splitView.masterPaneWidth = 180;
You can also include it by adding pod 'GSSplitViewController' to your Podfile.
ViewController.h
#property(nonatomic, assign) CGFloat maximumPrimaryColumnWidth NS_AVAILABLE_IOS(8_0);
ViewController.m
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
if (SYSTEM_VERSION_LESS_THAN(#"10.0")) {
[self setValue:[NSNumber numberWithFloat:200.0]forKey:#"_masterColumnWidth"];
}else{
self.maximumPrimaryColumnWidth = 200;
self.splitViewController.maximumPrimaryColumnWidth = self.maximumPrimaryColumnWidth;
}
Swift 3.0 you use like
let widthfraction = 2.0 //Your desired value for me 2.0
splitViewController?.preferredPrimaryColumnWidthFraction = 0.40
let minimumWidth = min((splitViewController?.view.bounds.size.width)!,(splitViewController?.view.bounds.height)!)
splitViewController?.minimumPrimaryColumnWidth = minimumWidth / widthFraction
splitViewController?.maximumPrimaryColumnWidth = minimumWidth / widthFraction
let leftNavController = splitViewController?.viewControllers.first as! UINavigationController
leftNavController.view.frame = CGRect(x: leftNavController.view.frame.origin.x, y: leftNavController.view.frame.origin.y, width: (minimumWidth / widthFraction), height: leftNavController.view.frame.height)
// in UISplitViewController subclass
// let more space for detail in portrait mode
- (void)viewWillLayoutSubviews
{
CGFloat width;
if (UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication.statusBarOrientation)){
width = CGRectGetWidth(self.view.bounds) * 0.25f;
}
else {
width = CGRectGetWidth(self.view.bounds) * 0.33f;
}
width = (NSInteger)fminf(260, fmaxf(120, width));
self.minimumPrimaryColumnWidth = width;
self.maximumPrimaryColumnWidth = width;
[super viewWillLayoutSubviews];
}
This code work for me:)
#interface UISplitViewController(myExt)
- (void)setNewMasterSize:(float)size;
#end
#implementation UISplitViewController(myExt)
- (void)setNewMasterSize:(float)size
{
_masterColumnWidth = size;
}
#end
and use it on each operation with view (like rotation)