UIView底部边界?
对于一个UIScrollView *toScrollView
(这是屏幕的宽度),我想添加一个灰色的底部边框(就像iPhone本地消息应用程序的撰写视图的to-field一样)。
为了达到这个目的,我跟着Cocoa Touch:如何改变UIView的边框颜色和厚度? 并使用自定义的UINavigationBar
覆盖顶部边框,并制作了toScrollView
的x坐标-1和宽度322,以便左右边框刚好在屏幕外。
这看起来不错,但这是一种黑客,我想知道是否有更好的方法来做到这一点。
- (void)viewDidLoad { [super viewDidLoad]; // Add UINavigationBar *navigationBar at top. self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAction)]; UINavigationBar *navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)]; navigationBar.items = [NSArray arrayWithObject:self.navigationItem]; // Add UIScrollView *toScrollView below navigationBar. UIScrollView *toScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(-1.0f, 43.0f, 322.0f, 45.0f)]; toScrollView.backgroundColor = [UIColor whiteColor]; toScrollView.layer.borderColor = [UIColor colorWithWhite:0.8f alpha:1.0f].CGColor; toScrollView.layer.borderWidth = 1.0f; [self.view addSubview:toScrollView]; [self.view addSubview:navigationBar]; // covers top of toScrollView }
而不是像@ImreKelényibuild议的那样使用UIView
,你可以使用CALayer
:
// Add a bottomBorder. CALayer *bottomBorder = [CALayer layer]; bottomBorder.frame = CGRectMake(0.0f, 43.0f, toScrollView.frame.size.width, 1.0f); bottomBorder.backgroundColor = [UIColor colorWithWhite:0.8f alpha:1.0f].CGColor; [toScrollView.layer addSublayer:bottomBorder];
在一个类别中实现:
的UIButton + Border.h:
@interface UIButton (Border) - (void)addBottomBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; - (void)addLeftBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; - (void)addRightBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; - (void)addTopBorderWithColor: (UIColor *) color andWidth:(CGFloat) borderWidth; @end
的UIButton + Border.m:
@implementation UIButton (Border) - (void)addTopBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(0, 0, self.frame.size.width, borderWidth); [self.layer addSublayer:border]; } - (void)addBottomBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(0, self.frame.size.height - borderWidth, self.frame.size.width, borderWidth); [self.layer addSublayer:border]; } - (void)addLeftBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(0, 0, borderWidth, self.frame.size.height); [self.layer addSublayer:border]; } - (void)addRightBorderWithColor:(UIColor *)color andWidth:(CGFloat) borderWidth { CALayer *border = [CALayer layer]; border.backgroundColor = color.CGColor; border.frame = CGRectMake(self.frame.size.width - borderWidth, 0, borderWidth, self.frame.size.height); [self.layer addSublayer:border]; }
希望它有帮助!
这是一个更广义的Swift扩展,为任何UIView
子类创build边界:
import UIKit extension UIView { func addTopBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.frame = CGRectMake(0, 0, self.frame.size.width, width) self.layer.addSublayer(border) } func addRightBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.frame = CGRectMake(self.frame.size.width - width, 0, width, self.frame.size.height) self.layer.addSublayer(border) } func addBottomBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.frame = CGRectMake(0, self.frame.size.height - width, self.frame.size.width, width) self.layer.addSublayer(border) } func addLeftBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.frame = CGRectMake(0, 0, width, self.frame.size.height) self.layer.addSublayer(border) } }
Swift 3
extension UIView { func addTopBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width) self.layer.addSublayer(border) } func addRightBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height) self.layer.addSublayer(border) } func addBottomBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width) self.layer.addSublayer(border) } func addLeftBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height) self.layer.addSublayer(border) } }
你可以添加一个独立的UIView
1点高度和灰色背景颜色toScrollView
并将其正下方toScrollView
。
编辑:除非你有一个很好的理由(想使用一些不提供CALayer的UIView的服务),你应该使用CALayer作为@MattDiPasqualebuild议 。 UIView有更大的开销,这在大多数情况下可能不是问题,但是,另一种解决scheme更加优雅。
如果您需要一个真正适应性的解决scheme(适用于所有屏幕尺寸),那就是:
/** * Extends UIView with shortcut methods * * @author Alexander Volkov * @version 1.0 */ extension UIView { /** Adds bottom border to the view with given side margins - parameter color: the border color - parameter margins: the left and right margin */ func addBottomBorder(color: UIColor = UIColor.lightBorderColor(), margins: CGFloat = 0) { let border = UIView() border.backgroundColor = color border.translatesAutoresizingMaskIntoConstraints = false self.addSubview(border) border.addConstraint(NSLayoutConstraint(item: border, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.Height, multiplier: 1, constant: 1)) self.addConstraint(NSLayoutConstraint(item: border, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)) self.addConstraint(NSLayoutConstraint(item: border, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: margins)) self.addConstraint(NSLayoutConstraint(item: border, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: margins)) } }
或者,性能最高的方式就是重载drawRect,就像这样:
@interface TPActionSheetButton : UIButton @property (assign) BOOL drawsTopLine; @property (assign) BOOL drawsBottomLine; @property (assign) BOOL drawsRightLine; @property (assign) BOOL drawsLeftLine; @property (strong, nonatomic) UIColor * lineColor; @end @implementation TPActionSheetButton - (void) drawRect:(CGRect)rect { CGContextRef ctx = UIGraphicsGetCurrentContext(); CGContextSetLineWidth(ctx, 0.5f * [[UIScreen mainScreen] scale]); CGFloat red, green, blue, alpha; [self.lineColor getRed:&red green:&green blue:&blue alpha:&alpha]; CGContextSetRGBStrokeColor(ctx, red, green, blue, alpha); if(self.drawsTopLine) { CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, CGRectGetMinX(rect), CGRectGetMinY(rect)); CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMinY(rect)); CGContextStrokePath(ctx); } if(self.drawsBottomLine) { CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, CGRectGetMinX(rect), CGRectGetMaxY(rect)); CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMaxY(rect)); CGContextStrokePath(ctx); } if(self.drawsLeftLine) { CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, CGRectGetMinX(rect), CGRectGetMinY(rect)); CGContextAddLineToPoint(ctx, CGRectGetMinX(rect), CGRectGetMaxY(rect)); CGContextStrokePath(ctx); } if(self.drawsRightLine) { CGContextBeginPath(ctx); CGContextMoveToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMinY(rect)); CGContextAddLineToPoint(ctx, CGRectGetMaxX(rect), CGRectGetMaxY(rect)); CGContextStrokePath(ctx); } [super drawRect:rect]; } @end
还有改进的代码,删除边框function。 基于confile答案 。
import UIKit enum viewBorder: String { case Left = "borderLeft" case Right = "borderRight" case Top = "borderTop" case Bottom = "borderBottom" } extension UIView { func addBorder(vBorder: viewBorder, color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor border.name = vBorder.rawValue switch vBorder { case .Left: border.frame = CGRectMake(0, 0, width, self.frame.size.height) case .Right: border.frame = CGRectMake(self.frame.size.width - width, 0, width, self.frame.size.height) case .Top: border.frame = CGRectMake(0, 0, self.frame.size.width, width) case .Bottom: border.frame = CGRectMake(0, self.frame.size.height - width, self.frame.size.width, width) } self.layer.addSublayer(border) } func removeBorder(border: viewBorder) { var layerForRemove: CALayer? for layer in self.layer.sublayers! { if layer.name == border.rawValue { layerForRemove = layer } } if let layer = layerForRemove { layer.removeFromSuperlayer() } } }
更新:Swift 3
import UIKit enum ViewBorder: String { case left, right, top, bottom } extension UIView { func add(border: ViewBorder, color: UIColor, width: CGFloat) { let borderLayer = CALayer() borderLayer.backgroundColor = color.cgColor borderLayer.name = border.rawValue switch border { case .left: borderLayer.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height) case .right: borderLayer.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height) case .top: borderLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width) case .bottom: borderLayer.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width) } self.layer.addSublayer(borderLayer) } func remove(border: ViewBorder) { guard let sublayers = self.layer.sublayers else { return } var layerForRemove: CALayer? for layer in sublayers { if layer.name == border.rawValue { layerForRemove = layer } } if let layer = layerForRemove { layer.removeFromSuperlayer() } } }
这些扩展方法的问题是当UIView / UIButton稍后调整它的大小时,你没有机会改变CALayer的大小来匹配新的大小。 这将给你留下一个错位的边界。 我发现最好是inheritance我的UIButton,你当然也可以inheritance其他的UIViews。 这里是一些代码:
enum BorderedButtonSide { case Top, Right, Bottom, Left } class BorderedButton : UIButton { private var borderTop: CALayer? private var borderTopWidth: CGFloat? private var borderRight: CALayer? private var borderRightWidth: CGFloat? private var borderBottom: CALayer? private var borderBottomWidth: CGFloat? private var borderLeft: CALayer? private var borderLeftWidth: CGFloat? func setBorder(side: BorderedButtonSide, _ color: UIColor, _ width: CGFloat) { let border = CALayer() border.backgroundColor = color.CGColor switch side { case .Top: border.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: width) borderTop?.removeFromSuperlayer() borderTop = border borderTopWidth = width case .Right: border.frame = CGRect(x: frame.size.width - width, y: 0, width: width, height: frame.size.height) borderRight?.removeFromSuperlayer() borderRight = border borderRightWidth = width case .Bottom: border.frame = CGRect(x: 0, y: frame.size.height - width, width: frame.size.width, height: width) borderBottom?.removeFromSuperlayer() borderBottom = border borderBottomWidth = width case .Left: border.frame = CGRect(x: 0, y: 0, width: width, height: frame.size.height) borderLeft?.removeFromSuperlayer() borderLeft = border borderLeftWidth = width } layer.addSublayer(border) } override func layoutSubviews() { super.layoutSubviews() borderTop?.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: borderTopWidth!) borderRight?.frame = CGRect(x: frame.size.width - borderRightWidth!, y: 0, width: borderRightWidth!, height: frame.size.height) borderBottom?.frame = CGRect(x: 0, y: frame.size.height - borderBottomWidth!, width: frame.size.width, height: borderBottomWidth!) borderLeft?.frame = CGRect(x: 0, y: 0, width: borderLeftWidth!, height: frame.size.height) } }
这是一个方便的UIView类别,可以让你在UIView的任何一边创build基于图层或基于视图的边框: UIView + Borders
我写了一个通用的方法,将在任何UIView
任何一边添加边框。 您可以为每一面定义厚度,颜色,边距和zOrder
。
/* view: the view to draw border around thickness: thickness of the border on the given side color: color of the border on the given side margin: space between the border's outer edge and the view's frame edge on the given side. zOrder: defines the order to add the borders to the view. The borders will be added by zOrder from lowest to highest, thus making the highest priority border visible when two borders overlap at the corners. */ +(void) drawBorderAroundUIView:(UIView *) view thicknessLeft:(CGFloat) thicknessLeft colorLeft:(UIColor *)colorLeft marginLeft:(CGFloat) marginLeft zOrderLeft:(int) zOrderLeft thicknessRight:(CGFloat) thicknessRight colorRight:(UIColor *)colorRight marginRight:(CGFloat) marginRight zOrderRight:(int) zOrderRight thicknessTop:(CGFloat) thicknessTop colorTop:(UIColor *)colorTop marginTop:(CGFloat) marginTop zOrderTop:(int) zOrderTop thicknessBottom:(CGFloat) thicknessBottom colorBottom:(UIColor *)colorBottom marginBottom:(CGFloat) marginBottom zOrderBottom:(int) zOrderBottom{ //make margins be the outside edge and make positive margin represent a smaller rectangle marginBottom = -1 * marginBottom - thicknessBottom; marginTop = -1 * marginTop - thicknessTop; marginLeft = -1 * marginLeft - thicknessLeft; marginRight = -1 * marginRight - thicknessRight; //get reference points for corners CGPoint upperLeftCorner = CGPointZero; CGPoint lowerLeftCorner = CGPointMake(upperLeftCorner.x, upperLeftCorner.y + view.frame.size.height); CGPoint upperRightCorner = CGPointMake(upperLeftCorner.x + view.frame.size.width, upperLeftCorner.y); //left CALayer *leftBorder = [CALayer layer]; leftBorder.frame = CGRectMake(upperLeftCorner.x - thicknessLeft - marginLeft, upperLeftCorner.y - thicknessTop - marginTop, thicknessLeft, view.frame.size.height + marginTop + marginBottom + thicknessBottom + thicknessTop); leftBorder.backgroundColor = colorLeft.CGColor; //right CALayer *rightBorder = [CALayer layer]; rightBorder.frame = CGRectMake(upperRightCorner.x + marginRight, upperRightCorner.y - thicknessTop - marginTop, thicknessRight, view.frame.size.height + marginTop + marginBottom + thicknessBottom + thicknessTop); rightBorder.backgroundColor = colorRight.CGColor; //top CALayer *topBorder = [CALayer layer]; topBorder.frame = CGRectMake(upperLeftCorner.x - thicknessLeft - marginLeft, upperLeftCorner.y - thicknessTop - marginTop, view.frame.size.width + marginLeft + marginRight + thicknessLeft + thicknessRight, thicknessTop); topBorder.backgroundColor = colorTop.CGColor; //bottom CALayer *bottomBorder = [CALayer layer]; bottomBorder.frame = CGRectMake(upperLeftCorner.x - thicknessLeft - marginLeft, lowerLeftCorner.y + marginBottom, view.frame.size.width + marginLeft + marginRight + thicknessLeft + thicknessRight, thicknessBottom); bottomBorder.backgroundColor = colorBottom.CGColor; //define dictionary keys to be used for adding borders in order of zOrder NSString *borderDK = @"border"; NSString *zOrderDK = @"zOrder"; //storing borders in dictionaries in preparation to add them in order of zOrder NSDictionary *leftBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:leftBorder, borderDK, [NSNumber numberWithInt:zOrderLeft], zOrderDK, nil]; NSDictionary *rightBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:rightBorder, borderDK, [NSNumber numberWithInt:zOrderRight], zOrderDK, nil]; NSDictionary *topBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:topBorder, borderDK, [NSNumber numberWithInt:zOrderTop], zOrderDK, nil]; NSDictionary *bottomBorderDictionary = [NSDictionary dictionaryWithObjectsAndKeys:bottomBorder, borderDK, [NSNumber numberWithInt:zOrderBottom], zOrderDK, nil]; NSMutableArray *borders = [NSMutableArray arrayWithObjects:leftBorderDictionary, rightBorderDictionary, topBorderDictionary, bottomBorderDictionary, nil]; //add borders in order of zOrder (lowest -> highest). Thus the highest zOrder will be added last so it will be on top. while (borders.count) { //look for the next lowest zOrder border to add NSDictionary *nextBorderToLayDown = [borders objectAtIndex:0]; for (int indexOfBorder = 0; indexOfBorder < borders.count; indexOfBorder++) { NSDictionary *borderAtIndex = [borders objectAtIndex:indexOfBorder]; if ([[borderAtIndex objectForKey:zOrderDK] intValue] < [[nextBorderToLayDown objectForKey:zOrderDK] intValue]) { nextBorderToLayDown = borderAtIndex; } } //add the border to the view [view.layer addSublayer:[nextBorderToLayDown objectForKey:borderDK]]; [borders removeObject:nextBorderToLayDown]; } }
Swile 3的Confile的答案是:
import UIKit extension UIView { func addTopBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: width) self.layer.addSublayer(border) } func addRightBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: self.frame.size.width - width, y: 0, width: width, height: self.frame.size.height) self.layer.addSublayer(border) } func addBottomBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: width) self.layer.addSublayer(border) } func addLeftBorderWithColor(color: UIColor, width: CGFloat) { let border = CALayer() border.backgroundColor = color.cgColor border.frame = CGRect(x: 0, y: 0, width: width, height: self.frame.size.height) self.layer.addSublayer(border) } }
使用自动布局时的用法:
class CustomView: UIView { override func awakeFromNib() { super.awakeFromNib() } override func layoutSubviews() { addBottomBorderWithColor(color: UIColor.white, width: 1) } }
您不必为每个边框添加图层,只需使用贝塞尔path绘制一次即可。
CGRect rect = self.bounds; CGPoint destPoint[4] = {CGPointZero, (CGPoint){0, rect.size.height}, (CGPoint){rect.size.width, rect.size.height}, (CGPoint){rect.size.width, 0}}; BOOL position[4] = {_top, _left, _bottom, _right}; UIBezierPath *path = [UIBezierPath new]; [path moveToPoint:destPoint[3]]; for (int i = 0; i < 4; ++i) { if (position[i]) { [path addLineToPoint:destPoint[i]]; } else { [path moveToPoint:destPoint[i]]; } } CAShapeLayer *borderLayer = [CAShapeLayer new]; borderLayer.frame = self.bounds; borderLayer.path = path.CGPath; borderLayer.lineWidth = _borderWidth ?: 1 / [UIScreen mainScreen].scale; borderLayer.strokeColor = _borderColor.CGColor; borderLayer.fillColor = [UIColor clearColor].CGColor; [self.layer addSublayer:borderLayer];
最完整的答案。 https://github.com/oney/UIView-Border
let rectangle = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 60)) rectangle.backgroundColor = UIColor.grayColor() view.addSubview(rectangle) rectangle.borderTop = Border(size: 3, color: UIColor.orangeColor(), offset: UIEdgeInsets(top: 0, left: -10, bottom: 0, right: -5)) rectangle.borderBottom = Border(size: 6, color: UIColor.redColor(), offset: UIEdgeInsets(top: 0, left: 10, bottom: 10, right: 0)) rectangle.borderLeft = Border(size: 2, color: UIColor.blueColor(), offset: UIEdgeInsets(top: 10, left: -10, bottom: 0, right: 0)) rectangle.borderRight = Border(size: 2, color: UIColor.greenColor(), offset: UIEdgeInsets(top: 10, left: 10, bottom: 0, right: 0))
斯威夫特4/3
你可以在下面使用这个解决scheme。 它在比层更轻的UIBezierPaths上工作,导致快速启动时间。 它很容易使用,请参阅下面的说明。
class ResizeBorderView: UIView { var color = UIColor.white var lineWidth: CGFloat = 1 var edges = [UIRectEdge](){ didSet { setNeedsDisplay() } } override func draw(_ rect: CGRect) { if edges.contains(.top) || edges.contains(.all){ let path = UIBezierPath() path.lineWidth = lineWidth color.setStroke() UIColor.blue.setFill() path.move(to: CGPoint(x: 0, y: 0 + lineWidth / 2)) path.addLine(to: CGPoint(x: self.bounds.width, y: 0 + lineWidth / 2)) path.stroke() } if edges.contains(.bottom) || edges.contains(.all){ let path = UIBezierPath() path.lineWidth = lineWidth color.setStroke() UIColor.blue.setFill() path.move(to: CGPoint(x: 0, y: self.bounds.height - lineWidth / 2)) path.addLine(to: CGPoint(x: self.bounds.width, y: self.bounds.height - lineWidth / 2)) path.stroke() } if edges.contains(.left) || edges.contains(.all){ let path = UIBezierPath() path.lineWidth = lineWidth color.setStroke() UIColor.blue.setFill() path.move(to: CGPoint(x: 0 + lineWidth / 2, y: 0)) path.addLine(to: CGPoint(x: 0 + lineWidth / 2, y: self.bounds.height)) path.stroke() } if edges.contains(.right) || edges.contains(.all){ let path = UIBezierPath() path.lineWidth = lineWidth color.setStroke() UIColor.blue.setFill() path.move(to: CGPoint(x: self.bounds.width - lineWidth / 2, y: 0)) path.addLine(to: CGPoint(x: self.bounds.width - lineWidth / 2, y: self.bounds.height)) path.stroke() } } }
- 将你的UIView的类设置为ResizeBorderView
- 通过在viewDidAppear方法中使用yourview.color和yourview.lineWidth来设置颜色和线宽
- 设置边缘,例如:yourview.edges = [.right,.left]([.all])
- 享受快速入门和调整边界