In my UITextView, I remove all padding and set the text to align to the right. On iOS 7, this works without a hitch. However, on iOS 6, the padding is still present on the right for an unknown reason. And when the text is just a certain size, it doesn't wrap properly because the height is calculated to fit on one line, but it is being displayed on two lines. Compare these screenshots (and to note, this occurs on both device and simulator):
I'm also setting zero margins on each of my text views using a 'utility' function as seen below:
+ (void) setZeroInsetsForTextView:(UITextView *) textView {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
// iOS 6
textView.contentInset = UIEdgeInsetsMake(-11.0, -8.0, -11.0, -8.0); // top, left, bottom, right
} else {
// iOS 7
textView.textContainer.lineFragmentPadding = 0.0;
textView.textContainerInset = UIEdgeInsetsZero;
}
}
Any ideas? I'm not quite sure what I need to do to address this or if I just forget iOS 6 people, as they are an ever-shrinking market.
Thank you. :)
Related
This one has been killing me for some time. I have a subclassed NSTextField with:
- (void)drawRect:(NSRect)dirtyRect {
if ([(SFIDisk *)self.cell.representedObject label]) {
// Calculate text width to draw label color around the text.
CGFloat textWidth = self.attributedStringValue.size.width + kSFIDiskViewTitleTextFieldCellMarginForTextSize;
CGFloat textStartX = dirtyRect.origin.x + ((dirtyRect.size.width - textWidth) * .5);
// Draw label background.
[SFIStyleKit drawDiskLabelWithFrame:NSMakeRect(textStartX, dirtyRect.origin.y, textWidth, dirtyRect.size.height)];
}
// Calculate text width to draw label color around the text.
CGFloat textStartY = dirtyRect.origin.y + ((dirtyRect.size.height - self.attributedStringValue.size.height) * .5);
CGFloat textWidth = self.attributedStringValue.size.width;
CGFloat textStartX = dirtyRect.origin.x + ((dirtyRect.size.width - textWidth) * .5);
// Due to the icon in attribtued string, center drawing for string needs to be manually done.
if (!self.currentEditor) {
// Since drawing happens in CALayer and it cannot support font smoothing, this feature needs to be turned off in order to work.
// https://stackoverflow.com/questions/715750/ugly-looking-text-when-drawing-nsattributedstring-in-cgcontext
CGContextSetShouldSmoothFonts((CGContextRef)[[NSGraphicsContext currentContext] graphicsPort], NO);
[self.attributedStringValue drawAtPoint:NSMakePoint(textStartX, textStartY)];
}
}
This seems to works perfect in the UI:
But when editing the NSTextView draws the string a bit heavier:
After editing all goes back to a thinner string:
Goal is to have both looking the same, so am assuming something is wrong in my draw string call since the editor is not manipulated by me.
I cannot figure this out for the life of me. Any ideas?
EDIT:
Did some extra tests by manually setting font for both NSTextField and its corresponding currentEditor when editing.
For every weight set for the font, currentEditor always displays the next weight level.
By finding this out i did a workaround but a proper answer to this is still needed.
Work around code
if ([(SFIDisk *)self.cell.representedObject label]) {
....
self.currentEditor.font = [NSFont systemFontOfSize:13 weight:NSFontWeightLight];
}
While you're editing, you'll be looking at the field editor, which is a NSTextView, rather than your NSTextField. Therefore, your override of -drawRect won't come into play until editing ends and the field editor is hidden again.
I wonder if anyone know how you move the legal sign on a mapview, right now my toolbar is covering it. Does anyone know how? There is lot's of help with the google logo but nothing on the apple maps.
In Swift:
mapView.layoutMargins = UIEdgeInsetsMake(top, right, -20, left)
I tested this in OS9 and it works.
Swift 5.2
// -20 will make the legal disclaimer move down. If you want the
// disclaimer to move up, use a positive number.
mapView.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: -20, right: 0)
This should work, although I'm not sure whether Apple will allow you to do that
UILabel *attributionLabel = [mapView.subviews objectAtIndex:1];
attributionLabel.center = CGPointMake(attributionLabel.center.x, attributionLabel.center.y - 44.0f);
This is still possible in iOS 7, but only (?) if placed in viewDidAppear.
The coords are reset if placed in viewDidLoad or viewWillAppear.
UILabel *attributionLabel = [mapView.subviews objectAtIndex:1];
attributionLabel.center = CGPointMake(attributionLabel.center.x, attributionLabel.center.y - 44.0f);
These methods no longer work on iOS 7. Correct way is to specify bottomLayoutGuide on your UIViewController.
Described in detail here
Changing the position doesn't quite work, however hiding the "Legal" button works perfectly.
[[mapView.subviews objectAtIndex:1] setHidden:YES]
EDIT:
Swift 2.0 iOS equivalent
mapView.subviews[1].isHidden = true
Carrying on Skeet Skeet point .
I implemented your approach it worked well but after coming in the viewcontroller multiple times legal label y keeps on decreasing as you see the logic it always displaces itself. so instead changing centre i propose
we change frame
UILabel *attributionLabel = [mapView.subviews objectAtIndex:1];
attributionLabel.frame = CGRectMake(20, self.view.frame.size.height - 135, attributionLabel.frame.size.width, attributionLabel.frame.size.height);
\\135 is height of your bottom view that was hiding legal
I wrote extension that worked for me. It can be used in animation block to animate those changes:
import MapKit
extension MKMapView {
/// Workaround for layoutMargins bug.
func setLegalInsets(left: CGFloat, bottom: CGFloat) {
let oldLeft = layoutMargins.left
let oldBottom = layoutMargins.bottom
let lblLegal = (subviews.filter { view in
return view is UILabel
}).first
lblLegal?.frame.origin.x += left - oldLeft
lblLegal?.frame.origin.y -= bottom - oldBottom
layoutMargins.left = left
layoutMargins.bottom = bottom
}
}
#Dymtro's answer works well for me, but I would suggest checking the size of the subviews first. This should at least prevent possible crashes if the view hierarchy changes in the future:
override func viewWillLayoutSubviews() {
positionLegalMapLabel()
}
func positionLegalMapLabel() {
if self.mapView.subviews.count > 1 {
let legalMapLabel = self.mapView.subviews[1]
legalMapLabel.frame.origin = CGPointMake(self.mapView.bounds.size.width - legalMapLabel.frame.size.width - 7, legalMapLabel.frame.origin.y)
}
}
Swift 4+
You can change the position of those by setting the layoutMargins of the mapView.
For example this will push it off from the bottom:
mapView.layoutMargins.bottom = -100
Also you can change edge insets you need all at once:
mapView.layoutMargins = UIEdgeInsets(top: 0, left: 0, bottom: -100, right: 0)
A Swift 3 example based on #xeieshan's example that works when compiled against iOS 10 SDK. In my example I have a transparent bar in the bottom that animates up when the map view is being present. The label repositioning can also be animated.
// reposition the 'Legal' label above the transparent bottom bar
// unfortunately there is no safe way to identify the label but it is the last subview - hopefully this will not change 😱
if let legalLabel = mapView.subviews.last {
var frame = legalLabel.frame
frame.origin.y = frame.origin.y - self.bottomBar.bounds.size.height // reposition it above the bottom bar
legalLabel.frame = frame
}
Use viewWillLayoutSubviews() instead of viewDidAppear() to avoid a jump.
override func viewWillLayoutSubviews() {
positionLegalMapLabel()
}
func positionLegalMapLabel() {
let legalMapLabel = self.mapView.subviews[1]
legalMapLabel.frame.origin = CGPointMake(self.mapView.bounds.size.width - legalMapLabel.frame.size.width - 7, legalMapLabel.frame.origin.y)
}
I'm working on a ios app minimum ios 5 and i have just hit a weird issue with uilabels. or maybe im missing something obvious. anyway the problem im having is that i have a uilabel which is to be centered text aligned. all works fine on ios 5 but on ios 6 it is always is left aligned. I seen that the old way of doing uilabel text align has deprecated and that setting it as so should work.
self.topComment.textAlignment = NSTextAlignmentCenter;
But no even this way it still only is center aligned on ios 5 and left aligned on ios 6.
I do have some code that resizes the font of the text in the label to try make it fit with a min and max size.
UIFont *font = self.topComment.font;
for(int i = maxFont; i > minFont; i--)
{
// Set the new font size.
font = [font fontWithSize:i];
CGSize constraintSize = CGSizeMake(self.topComment.frame.size.width, 1000);
CGSize labelSize = [topString sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
if(labelSize.height <= self.topComment.frame.size.height ) {
fits = YES;
break;
}
//self.topComment.text = topString;
}
self.topComment.font = font;
self.topComment.text = topString;
So that is the only thing i am doing to the label but it always is left aligned in ios 6. Important to note that if i drop in a uilabel with text and center align it and dont use the above code then it is centered on both ios 5 and 6.
Ok looks like turning on Tighten Letter Spacing has a different result on ios 5 and 6. I honestly cant explain why the difference but just turning this off gives the desired centering of labels for me. Just leaving this here as an answer in case anyone else makes the same silly mistake.
For example:
lbl.adjustsLetterSpacingToFitWidth = NO;
Note this is deprecated in iOS 7.0.
For iOS 6 or later:
label.textAlignment = NSTextAlignmentLeft;
In my UITableView I set the separatorStyle to UITableViewCellSeparatorStyleNone because in each row I want to show a set of pictures and it should like a grid without any separators.
In the simulator it looks like I want it to, but on my iPad (Version 5.0.1) I get a white line at the bottom of the HeaderCell. When I change the separatorColor to black the white line changes to black, which proves that it really is the separator. So it looks like my custom section view has a separator while the rows inside the sections do not.
I can "trick" the iPad to not show the separator when I define the header's height to 99 instead of 100 but that clearly is not the way to do it.
separatorColor = [UIColor clearColor]; ?
While I don't have the exact answer to your question, perhaps I can point you in the right direction for solving this issue.
I guess you've created custom cells? It sounds to me that it has something to do with the cell frame. In the speedy table view cell code from Tweetie I remember seeing the following code:
- (void)setFrame:(CGRect)newFrame
{
[super setFrame:newFrame];
CGRect bounds = self.bounds;
bounds.size.height -= 1; // keep space for de cell seperator
cellView.frame = bounds;
}
Perhaps Apple's default UITableViewCell code act the same when a frame is set and you could override it.
I'm trying to resize a UITextView when the keyboard shows. On iPhone it works beautifully. When the the system dispatches a keyboard notification, the text view resizes. When it's done editing, I resize it to fill in the initial space. (Yes, I'm assuming the keyboard is gone when the editing stops. I should change that. However, I don't think that's my issue.)
When I resize the textview on the iPad, the frame resizes correctly, but the app seems to reset the Y value of the frame to zero. Here's my code:
- (void) keyboardDidShowWithNotification:(NSNotification *)aNotification{
//
// If the content view being edited
// then show shrink it to fit above the keyboard.
//
if ([self.contentTextView isFirstResponder]) {
//
// Grab the keyboard size "meta data"
//
NSDictionary *info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
//
// Calculate the amount of the view that the keyboard hides.
//
// Here we do some confusing math voodoo.
//
// Get the bottom of the screen, subtract that
// from the keyboard height, then take the
// difference and set that as the bottom inset
// of the content text view.
//
float screenHeightMinusBottom = self.contentTextView.frame.size.height + self.contentTextView.frame.origin.y;
float heightOfBottom = self.view.frame.size.height - screenHeightMinusBottom;
float insetAmount = kbSize.height - heightOfBottom;
//
// Don't stretch the text to reach the keyboard if it's shorter.
//
if (insetAmount < 0) {
return;
}
self.keyboardOverlapPortrait = insetAmount;
float initialOriginX = self.contentTextView.frame.origin.x;
float initialOriginY = self.contentTextView.frame.origin.y;
[self.contentTextView setFrame:CGRectMake(initialOriginX, initialOriginY, self.contentTextView.frame.size.width, self.contentTextView.frame.size.height-insetAmount)];
}
Why would this work on iPhone, and not work on iPad? Also, can my autoresize masks be making an unexpected change?
Like said #bandejapaisa, I found that the orientation was a problem, at least during my tests.
The first thing, is about the use of kbSize.height being misleading, because in Landscape orientation it represents the width of the keyboard. So, as your code is in a UIViewController you can use it this way:
float insetAmount = (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)?kbSize.height:kbSize.width) - heightOfBottom;
The self.interfaceOrientation gives the orientation of the Interface (can be different from the Device orientation) and the macro UIInterfaceOrientationIsPortrait returns YES if the given orientation is Portrait (top or bottom). So as the keyboard height is in the kbSize.height when the interface is Portrait, and in the kbSize.width when the interface is Landscape, we simply need to test the orientation to get the good value.
But that's not enough, cause I've discovered the same problem with the self.view.frame.size.height value. So I used the same workaround:
float heightOfBottom = (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)?self.view.frame.size.height:self.view.frame.size.width) - screenHeightMinusBottom;
Hope this helps...