I am having a problem with my UITableViewCell's appearance on the iPad. This problem arose sometime between iOS 7 and now, but I don't know for sure when it started. On an iPod or iPhone my cells look fine (portrait or landscape), but on the iPad, the disclosure accessory is very wide. You can see an example in the image below. The tablecell has a green border and the contentview has a brown border. The content view is much smaller than it should be. Table cells without the disclosure accessory look fine.
I am using Xamarin, and I am creating this UI completely in code. Here is the code (I have omitted the code that lays out the inside of the cells ContentView since it isn't relevant:
protected void Draw()
{
Accessory = UITableViewCellAccessory.DisclosureIndicator;
Layer.BorderColor = UIColor.Green.CGColor;
Layer.BorderWidth = 1f;
Frame = new CGRect(0, 0, 320, 42);
ContentMode = UIViewContentMode.ScaleToFill;
AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth;
ContentView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;
ContentView.MultipleTouchEnabled = true;
ContentView.ContentMode = UIViewContentMode.Center;
ContentView.ClipsToBounds = true;
ContentView.Layer.BorderColor = UIColor.Brown.CGColor;
ContentView.Layer.BorderWidth = 1f;
}
I tried changing the size of the ContentView by overriding the cell's LayoutSubviews method. However, the ContentView was now as wide as the table, but the disclosure accessory didn't change and was now below the ContentView.
public override void LayoutSubviews()
{
base.LayoutSubviews();
//ContentView.Frame = new CGRect(0, 0, Frame.Size.Width, ContentView.Frame.Size.Height);
}
Again, this isn't a problem at all on the iPhone or iPod. I don't understand what I should be doing different for the iPad to work properly.
Thanks.
Zach Green
Edit - 10/21/2015 11:00
For the moment, I have made a very hacky change that is fixing the issue for me, but I know that future iOS updates will probably break this, so I would prefer a more iOS approved way to do this.
public override void LayoutSubviews()
{
base.LayoutSubviews();
//!! - Hack for ipad layout issue with disclosure indicator
if (Accessory == UITableViewCellAccessory.DisclosureIndicator && UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad)
{
ContentView.Frame = new CGRect(0, 0, Frame.Size.Width - 32, ContentView.Frame.Size.Height);
foreach (var view in Subviews.OfType<UIButton>())
{
view.Frame = new CGRect(Frame.Size.Width - 24, view.Frame.Y, 8, view.Frame.Height);
}
}
}
This is due to Apples new readable content margins in iOS9 intended to make content more readable in wider views (iPad Pro).
You can turn this feature off by setting
tableView.cellLayoutMarginsFollowReadableWidth = false
in viewDidLoad
Try this code:
protected void Draw()
{
Accessory = UITableViewCellAccessory.DisclosureIndicator;
Layer.BorderColor = UIColor.Green.CGColor;
Layer.BorderWidth = 1f;
Frame = new CGRect(0, 0, [UIApplication sharedApplication].keywindow.frame.size.width, 42);
ContentMode = UIViewContentMode.ScaleToFill;
AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth;
ContentView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth;
ContentView.MultipleTouchEnabled = true;
ContentView.ContentMode = UIViewContentMode.Center;
ContentView.ClipsToBounds = true;
ContentView.Layer.BorderColor = UIColor.Brown.CGColor;
ContentView.Layer.BorderWidth = 1f;
}
public override void LayoutSubviews()
{
base.LayoutSubviews();
//!! - Hack for ipad layout issue with disclosure indicator
if (Accessory == UITableViewCellAccessory.DisclosureIndicator && UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad)
{
ContentView.Frame = new CGRect(0, 0, [UIApplication sharedApplication].keywindow.frame.size.width - 32, ContentView.Frame.Size.Height);
foreach (var view in Subviews.OfType<UIButton>())
{
view.Frame = new CGRect([UIApplication sharedApplication].keywindow.frame.size.width - 24, view.Frame.Y, 8, view.Frame.Height);
}
}
}
Use the width same as that of [UIApplication sharedApplication].keyWindow.frame.size.width rather than hardcoding the values.
Related
Hi i'm trying to add a top and bottom margin to all my PackageCells.
like
what ive tried
%hook PackageCell
-(void)didMoveToWindow {
self.layer.cornerRadius = 15.0f;
self.layer.masksToBounds = true;
%orig;
}
-(CGRect)frame {
CGRect r = %orig;
return CGRectMake(40, 0, r.size.width, r.origin.height+20);
}
-(void)setFrame:(CGRect)frame {
CGRect r = frame;
%orig(CGRectMake(40, 0, r.size.width, r.origin.height+20));
}
%end
All cells get stacked on top of eachother and look weird.
You should set the backgroundColor of the cell to [UIColor clear]
And Change the frames of the contentView instead and make that view round
self.contentView.layer.cornerRadius = 15.0f;
And update the frame for this
self.contentView.frame = CGRectMake(40, 0, self.frame.size.width, self.frame.origin.height+20);
I am using Xamarin.IOS, I have UIprogressView inside a UIScrollView with two buttons as well.
I am setting the UIprogressView frame by code in ViewWillLayoutSubviews method:
CGRect newFrame;
newFrame.Width = MyScrollView.Frame.Width / 2;
newFrame.Location = new CGPoint(0, NextButton.Frame.Y);
MyProgressView.Frame = newFrame;
MyProgressView.Transform = CGAffineTransform.MakeScale(1, 20f);
MyProgressView.TrackTintColor = CustomColors.CustomColors.GetColor(CustomColors.CustomColors.ColorGray);
MyProgressView.ProgressTintColor = CustomColors.CustomColors.GetColor(CustomColors.CustomColors.ColorBlue);
The problem is when I rotate the device from portrait to landscape, only the progressview will disappear. Meanwhile the next button is there and both progress view and next button have same Y!.
If I stopped using MyProgressView.ProgressTintColor I can see the progress view after rotation but it is not scaled.
Any idea how to fix it?
This is the UIProgressView I am talking about:
Create this UIProgressView file in your Helper folder
LoadingOverlay.cs
public class LoadingOverlay : UIView
{
// control declarations
UIActivityIndicatorView activitySpinner;
UILabel loadingLabel;
public LoadingOverlay(CGRect frame) : base(frame)
{
// configurable bits
BackgroundColor = UIColor.Black;
Alpha = 0.75f;
AutoresizingMask = UIViewAutoresizing.All;
nfloat labelHeight = 22;
nfloat labelWidth = Frame.Width - 20;
// derive the center x and y
nfloat centerX = Frame.Width / 2;
nfloat centerY = Frame.Height / 2;
// create the activity spinner, center it horizontall and put it 5 points above center x
activitySpinner = new UIActivityIndicatorView(UIActivityIndicatorViewStyle.WhiteLarge);
activitySpinner.Frame = new CGRect(
centerX - (activitySpinner.Frame.Width / 2),
centerY - activitySpinner.Frame.Height - 20,
activitySpinner.Frame.Width,
activitySpinner.Frame.Height);
activitySpinner.AutoresizingMask = UIViewAutoresizing.All;
AddSubview(activitySpinner);
activitySpinner.StartAnimating();
// create and configure the "Loading Data" label
loadingLabel = new UILabel(new CGRect(
centerX - (labelWidth / 2),
centerY + 20,
labelWidth,
labelHeight
));
loadingLabel.BackgroundColor = UIColor.Clear;
loadingLabel.TextColor = UIColor.White;
loadingLabel.Text = "Loading Data...";
loadingLabel.TextAlignment = UITextAlignment.Center;
loadingLabel.AutoresizingMask = UIViewAutoresizing.All;
AddSubview(loadingLabel);
}
/// <summary>
/// Fades out the control and then removes it from the super view
/// </summary>
public void Hide()
{
UIView.Animate(
0.5, // duration
() => { Alpha = 0; },
() => { RemoveFromSuperview(); }
);
}
}
and Use this way
to show the loading
var loadingOverlay = new LoadingOverlay(UIScreen.MainScreen.Bounds);
View.Add(loadingOverlay);
and to hide it
loadingOverlay.Hide();
I fixed it by re-scale it at every rotate:
public override void WillAnimateRotation(UIInterfaceOrientation toInterfaceOrientation, double duration)
{
progressView.Transform = CGAffineTransform.Scale(progressView.Transform,1, 20f);
}
I am trying to add padding to UITextField. I did some research and found the following code.
UIView paddingView = new UIView(new RectangleF(0, 0, 5, 20));
txtBoxUsername.LeftView = paddingView;
txtBoxUsername.LeftViewMode = UITextFieldViewMode.Always;
This code works fine till there is a border for UITextField. My UiTextField does not have border. As soon as I set Border None this padding effect is also lost. I am setting border none with following code.
txtBoxUsername.BorderStyle = UITextBorderStyle.None;
Can anybody please tell me how to add padding to UITextField with no borders.
Following code worked for me
public class TextBox : UITextField
{
public TextBox(string placeholder, UIFont font = null)
{
Layer.BorderWidth = 0f;
AttributedPlaceholder = new NSAttributedString(
placeholder,
font: UIFont.ItalicSystemFontOfSize(15.0f)
);
if (font != null)
{
Font = font;
}
UIView paddingView = new UIView(new CGRect(0, 0, 10.0f, 20.0f));
LeftView = paddingView;
LeftViewMode = UITextFieldViewMode.Always;
}
}
I've got a label on a prototype cell in a table view. I've got some random text in there that I'd like to see change based on the size of the system font. Right now, if I change the system font size, this label stays the same size.
This is mostly for iOS 8. If this works with iOS 7, it'd be even better.
In iOS 7 and later, you can use dynamic type:
cell.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
See WWDC 2014 video, What's New in Table and Collection Views for an illustration of how you would do this.
The question is how to change the cell height. In iOS 8, the default tableview cells will change size automatically. If you are using your own prototype cell with custom layout, if you use auto layout and have fully defined constraints for the content view, then then table view height will change automatically for you.
You could use something like this if I got what you mean.
if UIScreen.mainScreen().bounds.size.height == 480 {
// iPhone 4
label.font = label.font.fontWithSize(20)
} else if UIScreen.mainScreen().bounds.size.height == 568 {
// IPhone 5
label.font = label.font.fontWithSize(20)
} else if UIScreen.mainScreen().bounds.size.width == 375 {
// iPhone 6
label.font = label.font.fontWithSize(20)
} else if UIScreen.mainScreen().bounds.size.width == 414 {
// iPhone 6+
label.font = label.font.fontWithSize(20)
} else if UIScreen.mainScreen().bounds.size.width == 768 {
// iPad
}
In the WWDC 2014, Apple introduced a new feature called Self Sizing Cells for the UITableView and the UICollectionView. You could add this to your -(void)viewDidLoad:
tableView.estimatedRowHeight = 44.0; //Or anything else
tableView.rowHeight = UITableViewAutomaticDimension;
Helpful article: Understanding Self Sizing Cells and Dynamic Type in iOS 8
Best regards,
Not sure if you are using a specified font. If so, here's the method I use :
+ (int) preferredFontSize : (float) fontSize
{
NSString *category = [[UIApplication sharedApplication] preferredContentSizeCategory];
if ([category isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge])
{
return fontSize / 0.7;
}
else
{
if ([category isEqualToString:UIContentSizeCategoryExtraExtraLarge])
{
return fontSize / 0.8;
}
else
{
if ([category isEqualToString:UIContentSizeCategoryExtraLarge])
{
return fontSize / 0.9;
}
else
{
if ([category isEqualToString:UIContentSizeCategoryMedium])
{
return fontSize * 0.9;
}
else
{
if ([category isEqualToString:UIContentSizeCategorySmall])
{
return fontSize * 0.8;
}
else
{
if ([category isEqualToString:UIContentSizeCategoryExtraSmall])
{
return fontSize * 0.7;
}
else
{
return fontSize;
}
}
}
}
}
}
}
#end
I have a custom UITableViewCell in which I want to draw a vertical separator, similar to the default horizontal ones in iOS7. Currently I use this code when I configure the cell:
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(cell.contentView.bounds.size.width - rightButtonWidth, 0, 1, cell.contentView.bounds.size.height)];
lineView.backgroundColor = [UIColor lightGrayColor];
lineView.autoresizingMask = 0x3f;
[cell.contentView addSubview:lineView];
As seen in the image, the default separator is rendered at 1 pixel height, whereas mine gets two pixels wide. I tried setting the width to .5 points instead, but then the line is not rendered at all.
Also the color is off, obviously not lightGrayColor. Is there a color constant in UIColor that matches? Edit: the color is RGB 207,207,210 which does not seem to be listed in UIColor.h.
Your problem is because the view will have a width on retina of 2px if the width is set to 1px. What I would suggest is to create a subclass of UIView, let's call it CustomDivider and in -layoutSubviews you will do something like this:
-(void)layoutSubviews {
[super layoutSubviews];
if([self.constraints count] == 0) {
CGFloat width = self.frame.size.width;
CGFloat height = self.frame.size.height;
if(width == 1) {
width = width / [UIScreen mainScreen].scale;
}
if (height == 0) {
height = 1 / [UIScreen mainScreen].scale;
}
if(height == 1) {
height = height / [UIScreen mainScreen].scale;
}
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, width, height);
}
else {
for(NSLayoutConstraint *constraint in self.constraints) {
if((constraint.firstAttribute == NSLayoutAttributeWidth || constraint.firstAttribute == NSLayoutAttributeHeight) && constraint.constant == 1) {
constraint.constant /=[UIScreen mainScreen].scale;
}
}
}
}
The code snippet above will check which dimension (width or height) is less or equal to 1 and it will resize it depending on the screen resolution. Also this code will work with autolayout (tested).
This approach will work from IB and from code.
Like #danypata did subclass works fine but my situation was to fix exist code quick and simple. Alternatively,
- (CGFloat)defaultOnePixelConversion
{
return 1.f / [UIScreen mainScreen].scale;
}
then directly apply to the line view.
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, /*1*/[self defaultOnePixelConversion], height);
the result came out not much different than default UITableView's separator.
If you use custom UITableViewCells you can do it via their .xib. Just add a UIView with the color you want, make it one pixel and position it where you want. No need to do it programmatically.
In your code snippet you should add this line:
lineView.clipsToBounds = YES;
Here is the swift 4 version:
override func layoutSubviews() {
super.layoutSubviews()
if self.constraints.count == 0 {
var width = self.frame.size.width
var height = self.frame.size.height
if width == 1{
width = width / UIScreen.main.scale
}
if height == 0{
height = 1 / UIScreen.main.scale
}
if height == 1 {
height = height / UIScreen.main.scale
}
self.frame = CGRect(x:self.frame.origin.x, y:self.frame.origin.y, width:width, height:height);
}
else{
for constraint:NSLayoutConstraint in self.constraints{
if(constraint.firstAttribute == NSLayoutAttribute.width || constraint.firstAttribute == NSLayoutAttribute.height) && constraint.constant == 1{
constraint.constant /= UIScreen.main.scale
}
}
}
}