iOS 11 space between UIBarButtonItem - uinavigationbar

I've noticed that iOS 11 has made some change to the UIBarButtonItem. After solving the sizing problem of the image of UIBarButtonItem, I find myself facing another even more strange issue.
We have a UIToolBar with several UIBarButtonItems and we used to set the width of the UIBarButtonItem as 40 and the width of the UIButton.image as 24, which leaves a nice spacing between every two UIBarButtonItems. However, in iOS 11, the space disappear.
I tried with
[self.deleteButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.size.mas_equalTo(CGSizeMake(40, 24));
}];
NSLayoutConstraint *w = [self.deleteButton.imageView.widthAnchor constraintEqualToConstant:24];
NSLayoutConstraint *h = [self.deleteButton.imageView.widthAnchor constraintEqualToConstant:24];
w.active = YES;
h.active = YES;
but it doesn't work as I thought.
I either get a list of stretched images with CGSize(40, 24) or a list of UIBarButtonItem with CGSize(24, 24) lining up in the UINavigationBar one by one without spacing.
Are there anything other constraints I need to add to create the spacing?

Try this:
UIButton*(^buttonWith)(NSString *) = ^(NSString *imageName) {
CGFloat size = 40.0;
UIButton *button = [[UIButton alloc] initWithFrame: CGRectMake(0.0, 0.0, size, size)];
[button setImage: [[UIImage imageNamed: imageName] imageWithRenderingMode: UIImageRenderingModeAlwaysOriginal] forState: UIControlStateNormal];
[button addConstraint: [NSLayoutConstraint constraintWithItem: button attribute: NSLayoutAttributeWidth relatedBy: NSLayoutRelationEqual toItem: nil attribute: NSLayoutAttributeNotAnAttribute multiplier: 1.0 constant: size]];
[button addConstraint: [NSLayoutConstraint constraintWithItem: button attribute: NSLayoutAttributeHeight relatedBy: NSLayoutRelationEqual toItem: nil attribute: NSLayoutAttributeNotAnAttribute multiplier: 1.0 constant: size]];
return button;
};
Usage:
UIButton *resetButton = buttonWith(#"reset");
self.resetBarButton = [[UIBarButtonItem alloc] initWithCustomView: resetButton];
UIButton *backButton = buttonWith(#"back");
self.backBarButton = [[UIBarButtonItem alloc] initWithCustomView: backButton];
self.navigationItem.leftBarButtonItems = #[self.backBarButton, self.resetBarButton];

In storyboard you can place another UIBarButtonItem in between the buttons that need spacing. set the text to spaces only. see screen shots.

Related

Scrolling of scrollView with TextView

I'm having scrollView contains one UIImageView on Top and UITextView with scrollingEnabled = NO , I want to scroll scrollView at the position where I type.
- (void)createScrollView{
//TPKeyboardAvoidingScrollView *scrollView = [[TPKeyboardAvoidingScrollView alloc]init];
UIScrollView *scrollView = [[UIScrollView alloc]init];
//[self.view insertSubview:scrollView belowSubview:_mediaSelectionView];
[self.view addSubview: scrollView];
[scrollView setTranslatesAutoresizingMaskIntoConstraints: NO];
self.scrollView = scrollView;
scrollView.showsHorizontalScrollIndicator = NO;
scrollView.showsVerticalScrollIndicator = NO;
scrollView.bouncesZoom = NO;
scrollView.alwaysBounceVertical = YES;
scrollView.clipsToBounds = YES;
self.automaticallyAdjustsScrollViewInsets = YES;
NSLayoutConstraint *scrollViewTop = [NSLayoutConstraint
constraintWithItem: scrollView
attribute: NSLayoutAttributeTop
relatedBy: NSLayoutRelationEqual
toItem: self.navigationBarBGView
attribute: NSLayoutAttributeBottom
multiplier: 1 constant:0.0
];
NSLayoutConstraint *scrollViewLeading = [NSLayoutConstraint
constraintWithItem: scrollView
attribute: NSLayoutAttributeLeading
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeLeading
multiplier: 1 constant:0.0
];
NSLayoutConstraint *superViewTraling = [NSLayoutConstraint
constraintWithItem: self.view
attribute: NSLayoutAttributeTrailing
relatedBy: NSLayoutRelationEqual
toItem: scrollView
attribute: NSLayoutAttributeTrailing
multiplier: 1 constant:0.0
];
NSLayoutConstraint *bottomLayoutGuideTop = [NSLayoutConstraint
constraintWithItem:self.view
attribute: NSLayoutAttributeBottom
relatedBy: NSLayoutRelationEqual
toItem: scrollView
attribute: NSLayoutAttributeBottom
multiplier: 1 constant:0.0
];
//Add All Constrains.
[self.view addConstraints: #[scrollViewTop , scrollViewLeading , superViewTraling , bottomLayoutGuideTop ]];
_contentView = [[UIView alloc]init];
[scrollView addSubview: _contentView];
[_contentView setConstraintFlag];
[_contentView setFullWidth];
[_contentView setTopFromParent:0];
}
- (void)createCommentTextView{
UITextView *textView = [[UITextView alloc]init];
textView.backgroundColor = [UIColor clearColor];
textView.textColor = [UIColor colorWithR:67 G:83 B:83 A:1.0f];
textView.delegate = self;
textView.scrollEnabled = NO;
_commentTextView = textView;
[_textViewContainer addSubview:textView];
}
-(void)updateContentSize{
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, self.contentView.frame.size.height);
}
scrollView contains _contentView and contentView contains UITextView. textView height increases as user types and _contentView's bottom is equal to bottom of textView.
use:
pod 'TPKeyboardAvoiding' or
https://github.com/michaeltyson/TPKeyboardAvoiding
pod 'AnimatedTextInput'
https://github.com/jobandtalent/AnimatedTextInput
I have gone through several posts and blogs, but I didn't get exact answer I want. After that, I came up with some sort of soln with the help of other posts and blogs. Hope this will help.
- (void)scrollToCursor{
if(self.currentTextView.selectedRange.location != NSNotFound) {
NSRange range = self.currentTextView.selectedRange;
NSString *string = [<YOUR_TEXTVIEW>.text substringToIndex:range.location];
CGSize size = [string boundingRectWithSize:<YOUR_TEXTVIEW>.frame.size
options:NSStringDrawingUsesLineFragmentOrigin| NSStringDrawingUsesFontLeading
attributes:#{NSFontAttributeName:<YOUR_TEXTVIEW>.font}
context:nil].size;
NSInteger yPosition = (<YOUR_TEXTVIEW>.frame.origin.y+size.height+<OFFSET(if required)>);
NSInteger scrollViewVisibeYPositionStart = self.scrollView.contentOffset.y;
NSInteger scrollViewVisibeYPositionEnd = scrollViewVisibeYPositionStart+self.scrollView.frame.size.height;
BOOL needToSetOffset = NO;
if (yPosition > scrollViewVisibeYPositionEnd) {
yPosition = yPosition - self.scrollView.frame.size.height;
needToSetOffset = YES;
}else if (yPosition < scrollViewVisibeYPositionStart){
yPosition = self.scrollView.frame.size.height + (scrollViewVisibeYPositionStart - yPosition);
needToSetOffset = YES;
}
if (needToSetOffset) {
CGPoint offset = CGPointMake(0,yPosition);
[self.scrollView setContentOffset:offset];
}
}
}

UIPickerView inside prototype tableviewcell has no selection indicator

I have a table view controller that has multiple prototype cells for different input controls like UISwitch, UITextField, and UIPickerView, when the view is loaded I determine which control is needed and only present that one prototype cell.
However for the cell with the UIPickerView I cannot get the selection indicator to appear and the pickerview ends up looking like this:
In my tableView:cellForRowAtIndexPath: method I explicitly enable the selection indicator:
UIPickerView * pickerView = [cell viewWithTag:InputTagPicker];
pickerView.dataSource = self;
pickerView.delegate = self;
[pickerView selectRow:self.pickerInitialIndex inComponent:0 animated:YES];
[pickerView setShowsSelectionIndicator:YES];
Why is the selection indicator not showing up?
I had a similar problem. I noticed that every time I would swipe to a different tab, this problem would go away. This meant that something funny was happening as the tableview(cell) was appearing into view.
Since UITableViewCell doesn't have a viewWillAppear() method, I managed to solve this problem by force removing the pickerView from the cell's list of subviews and then adding it back. This simulates the process of setting the selection before the pickerView is added as a subview to the cell, and fixes the issue.
Here is what you can try:
UIPickerView * pickerView = [cell viewWithTag:InputTagPicker];
pickerView.dataSource = self;
pickerView.delegate = self;
[pickerView selectRow:self.pickerInitialIndex inComponent:0 animated:YES];
[pickerView setShowsSelectionIndicator:YES];
// hack to make sure the selection indicator shows
// first remove the picker
[pickerView removeFromSuperview];
// then add it back
[cell.contentView addSubview:pickerView];
However, this would create the picker that is stuck to the left edge of the cell, so you need to programmatically set the constraints
// finally set the constraints to make sure it fits horizontally centered
//Trailing
NSLayoutConstraint *trailing =[NSLayoutConstraint
constraintWithItem:pickerView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeTrailing
multiplier:1.0f
constant:0.f];
//Leading
NSLayoutConstraint *leading = [NSLayoutConstraint
constraintWithItem:pickerView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant:0.f];
//Center Y
NSLayoutConstraint *centerY = [NSLayoutConstraint
constraintWithItem:pickerView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeCenterY
multiplier:1.0f
constant:0.f];
[cell.contentView addConstraints:trailing];
[cell.contentView addConstraints:leading];
[cell.contentView addConstraints:centerY];
Here is a Swift 4 version:
// first remove the picker
pickerView?.removeFromSuperview()
// then add it back
cell.contentView.addSubview(pickerView!)
// and finally set the constraints to make sure it fits horizontally centered
cell.contentView.addConstraints([NSLayoutConstraint.init(item: pickerView as Any, attribute: .centerY, relatedBy: .equal, toItem: cell.contentView, attribute: .centerY, multiplier: 1, constant: 0),
NSLayoutConstraint.init(item: pickerView as Any, attribute: .trailing, relatedBy: .equal, toItem: cell.contentView, attribute: .trailing, multiplier: 1, constant: 0),
NSLayoutConstraint.init(item: pickerView as Any, attribute: .leading, relatedBy: .equal, toItem: cell.contentView, attribute: .leading, multiplier: 1, constant: 0)])

iOS How to align UIViews / UILabels along a angled axis?

I would like to setup a series of UILabels that have their X-centers aligned along a 45degree angel.
I imagine I can do this mathematically, but is there a way I can draw a 45degree line and then assign each UILabel to be positioned at different points along that 45degree line?
You could set up a constraint for your label's center-x, center-y to be a percentage of the containing view's width / height. Here's a quick example in the context of a generic view controller:
for ( CGFloat m = 0.25 ; m < 1 ; m += .25 ) {
UILabel* label = [[UILabel alloc] initWithFrame: CGRectMake(0, 0, 100, 40)];
label.text = #"Hello, World";
label.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview: label];
NSLayoutConstraint* lcx = [NSLayoutConstraint constraintWithItem: label
attribute: NSLayoutAttributeCenterX
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeRight
multiplier: m
constant: 0];
NSLayoutConstraint* lcy = [NSLayoutConstraint constraintWithItem: label
attribute: NSLayoutAttributeCenterY
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeBottom
multiplier: m
constant: 0];
[self.view addConstraint: lcx];
[self.view addConstraint: lcy];
}

iOS:Add a frame based subview to a constraint based superview

I have a constraint based super view which looks like
CGFloat _expandedHeight = 500;
NSLayoutConstraint *_heightConstraint =
[NSLayoutConstraint constraintWithItem: superView
attribute: NSLayoutAttributeHeight
relatedBy: NSLayoutRelationEqual
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 0.0
constant: _expandedHeight];
[superView addConstraint: _heightConstraint];
it's size and origin are not determined(Actually, it's the system input view in custom keyboard extension).
Is there any possibility can I add a frame based subview such as
UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
to that constraint based super view?
I tried to directly add subView to the superView but got a unable to simultaneously satisfy constraints exception. Then I add
subView.translatesAutoresizingMaskIntoConstraints = NO;
but it still doesn't work.

UIButton with both image and text possible?

I have a button of size width 200 and height 270. I want to have text and image on the same button. Not as a background image on that text. Instead of this, I want to display the text with height 120 and image of height 150 on the same button. How to do it?
You can use this code,it will serve your purpose
.h file code
IBOutlet UIButton *testBtn;
.m file code
[testBtn setImage: [UIImage imageNamed:#"Image name.png"] forState:UIControlStateNormal];
[testBtn setTitleEdgeInsets:UIEdgeInsetsMake(70.0, -150.0, 5.0, 5.0)];
[testBtn setTitle:#"Your text" forState:UIControlStateNormal];
Adjust the coordinates and size of your button as your requirement.This is a sample code to guide you.
PS:Take a UIButton in the nib file>Make it a custom button>Connect the IBOutlet to your custom button in the nib file.Thats it.
The below code shows placing an image and text in a button using setImageEdgeInsets and setTitleEdgeInsets properties of UIButton class.
UIButton *butt=[UIButton buttonWithType:UIButtonTypeCustom ];
[butt setFrame:CGRectMake(0, 0, 100, 100)];
[butt setBackgroundImage:[UIImage imageNamed:#"sig.png"] forState:UIControlStateNormal];
[butt setImageEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, 50.0, 0.0)];
[butt setTitleEdgeInsets:UIEdgeInsetsMake(75.0, 0.0, 0.0, 0.0)];
[butt setTitle:#"hello" forState:UIControlStateNormal];
[butt setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[self.view addSubview:butt];
In Swift 3
let button = UIButton()
//make sure the image (jpg, png) you are placing in the button
//is about the right size or it will push the label off the button
//add image
let image = UIImage(named:"my_button_image")
button.setImage(image, for: .normal)
button.imageView?.contentMode = .scaleAspectFit
button.imageEdgeInsets = UIEdgeInsets(top:0, left:-30, bottom:0, right:0) //adjust these to have fit right
//add title
button.setTitle("Fan", for: .normal)
button.titleEdgeInsets = UIEdgeInsets(top:0, left:10, bottom:0, right:0) //adjust insets to have fit how you want
//add button to your view - like in viewDidLoad or your own custom view setup
addSubview(button)
Don't forget to set anchors if doing so programmatically for it to appear/attach it to your button reference in Interface Builder if using a storyboard
Yes, you can display an image and a title in the same button, as long as they are both small enough to fit. The hard part comes, when you need to deal with placement.
You can play with the contentVerticalAlignment and contentHorizontalAlignment properties of the UIButton (inherited from UIControl).
You can also use the titleEdgeInsets and imageEdgeInsets properties to shift the the title and image from the placement they get based on the alignment properties.
If you want very accurate or custom placement, I suggest overriding the titleRectForContentRect: and imageRectForContentRect: methods of the UIButton. These allow you to define the frames for your title and image, and they are called whenever the button needs to be laid out. One warning, if you want to reference self.titleLabel inside of titleRectForContentRect:, make sure self.currentTitle is defined first. I was getting a bad access error, , possibly the method is called when first initializing the titleLabel.
Use the concept of subviews. Take 3 controls, UIButton (200x270), UILabel (200x120) and UIImageView (200x150). Assign the image to the UIImageView and set the text for the UILabel. Position your label and imageview controls based upon the (x,y) location of the UIButton.
In the end, you should have something like:
For the following instances:
UIButton *button;
UILabel *label;
UIImageView *imageView;
[button addSubView:imageView];
[button addSubView:label];
[testBtn setBackgroudImage: [UIImage imageNamed:#"Image name.png"] forState:UIControlStateNormal];
[testBtn setTitle:#"Your text" forState:UIControlStateNormal];
If you want to have more control over the layout, create a subclass of UIControl and arrange the views you want (UILabel, UIImageView) in a xib file. Then you can use Autolayout regularly without having to sort out UIEdgeInsets.
swift version:
var button = UIButton()
newGameButton.setTitle("Новая игра", for: .normal)
newGameButton.setImage(UIImage(named: "energi"), for: .normal)
newGameButton.backgroundColor = .blue
newGameButton.imageEdgeInsets.left = -50
Using button subviews worked for me in Swift 4.
Create your button
Set an image for button
Create a label for the text
Add the label as a subview of your button
Constrain the label within the button
let imageAndTextButton : UIButton = {
let button = UIButton(frame: .zero)
button.tintColor = .clear
button.setImage(#imageLiteral(resourceName: "buttonImage"), for: .normal)
button.imageView?.contentMode = .scaleToFill
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let textForButtonLabel = UILabel(frame: .zero)
textForButtonLabel.translatesAutoresizingMaskIntoConstraints = false
textForButtonLabel.attributedText = NSAttributedString(string: "Text For Button", attributes: buttonTextAttributes)
textForButtonLabel.textAlignment = .center
textForButtonLabel.backgroundColor = .clear
imageAndTextButton.addSubview(textForButtonLabel)
imageAndTextButton.addConstraint(NSLayoutConstraint(item: textForButtonLabel, attribute: .centerX, relatedBy: .equal, toItem: imageAndTextButton, attribute: .centerX, multiplier: 1.0, constant: 0))
imageAndTextButton.addConstraint(NSLayoutConstraint(item: textForButtonLabel, attribute: .centerY, relatedBy: .equal, toItem: imageAndTextButton, attribute: .centerY, multiplier: 1.0, constant: 0))
Try this it's work for me ,
We have to set value of UIEdgeInsets base on our button size and requirement .
[self.testButton setTitle: #"myTitle" forState: UIControlStateNormal];
UIImage *iconImage = [UIImage imageWithIcon:#"fa-dot-circle-o" backgroundColor:[UIColor clearColor] iconColor:[UIColor whiteColor] fontSize:10.0f];
//UIEdgeInsets insets = {top, left, bottom, right};
[self.testButton setImageEdgeInsets:UIEdgeInsetsMake(3.0, 0.0, 3.0, 2.0)];
[self.testButton setTitleEdgeInsets:UIEdgeInsetsMake(3.0, 1.0, 3.0, 0.0)];
[self.testButton setImage:iconImage forState:UIControlStateNormal];
[self.testButton addTarget:self action:#selector(testButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.testButton];
Hope This will help for some one .

Resources