NSTextField Custom focus ring - objective-c

is there a possibility to draw a custom focus ring in an editable NSTextField? I searched the whole net, but couldn't find a working solution. I subclassed the NSTextField and overrided "drawFocusRingMask", but without any result.
My target is to implement a focus ring like the one in the Mac OS Adressbook (while editing a person)

This code in the subclass of NSTextField works:
- (void)awakeFromNib {
self.focusRingType = NSFocusRingTypeNone;
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
BOOL focus = NO;
//check, if the NSTextField is focused
id firstResponder = self.window.firstResponder;
if ([firstResponder isKindOfClass:[NSTextView class]]) {
NSTextView *textView = (NSTextView*)firstResponder;
NSClipView *clipView = (NSClipView*)textView.superview;
NSTextField *textField = (NSTextField*)clipView.superview;
if (textField == self)
focus = YES;
}
if (focus) {
NSRect bounds = self.bounds;
NSRect outerRect = NSMakeRect(bounds.origin.x - 2,
bounds.origin.y - 2,
bounds.size.width + 4,
bounds.size.height + 4);
NSRect innerRect = NSInsetRect(outerRect, 1, 1);
NSBezierPath *clipPath = [NSBezierPath bezierPathWithRect:outerRect];
[clipPath appendBezierPath:[NSBezierPath bezierPathWithRect:innerRect]];
[clipPath setWindingRule:NSEvenOddWindingRule];
[clipPath setClip];
[[NSColor colorWithCalibratedWhite:0.6 alpha:1.0] setFill];
[[NSBezierPath bezierPathWithRect:outerRect] fill];
}
}

Above answer is work well!
Below is Swift 3.0 version that I converted
class MyTextField: NSTextField {
override func awakeFromNib() {
self.focusRingType = NSFocusRingType.none
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
var focus = false
if let firstResponder = self.window?.firstResponder {
if firstResponder.isKind(of: NSTextView.self) {
let textView = firstResponder as! NSTextView
if let clipView = textView.superview {
if let textField = clipView.superview {
if textField == self {
focus = true
}
}
}
}
}
if focus {
let bounds = self.bounds
let outerRect = NSMakeRect(bounds.origin.x - 2, bounds.origin.y - 2, bounds.size.width + 4, bounds.size.height + 4)
let innerRect = NSInsetRect(outerRect, 1, 1)
let clipPath = NSBezierPath(rect: outerRect)
clipPath.append(NSBezierPath(rect: innerRect))
clipPath.windingRule = NSWindingRule.evenOddWindingRule
clipPath.setClip()
NSColor(calibratedWhite: 0.6, alpha: 1.0).setFill()
NSBezierPath(rect: outerRect).fill()
self.backgroundColor = NSColor(cgColor: CGColor(red: 1, green: 0.4, blue: 0.5, alpha: 0.4))
}
}
}
and How to use is as below
textView.select(withFrame: textView.frame, editor: NSText(), delegate: self, start, 0, length: 3)

Related

Device tab bar icons washed out on iPhone 6 device but not simulator [duplicate]

My application has a toolbar with image buttons on them (subclass of UIButton); when the user switches on the "Bold text" accessibility option, not only the text becomes bold but the images follow suit.
This is the toolbar in normal mode:
When the "bold text" is enabled:
It seems to be caused by my UIButton subclass, which is included below. I'm using this class to apply an image tint colour when the button is clicked, disabled, etc. and prevents having to include multiple states of each button. For that I'm using the UIImageRenderingModeAlwaysTemplate which reportedly exhibits this observed behaviour.
I've tried to uncheck the "Accessibility" option in the interface builder, but that had no effect at all. Is there a way to fix this?
#import "AppButton.h"
#implementation AppButton
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
[self initialize];
}
return self;
}
- (void)initialize
{
self.adjustsImageWhenHighlighted = NO;
[self setImage:[[self imageForState:UIControlStateNormal] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
}
- (void)updateButtonView
{
if (!self.enabled) {
self.imageView.tintColor = [UIColor colorWithRGBValue:RGBValueC9];
} else if (self.highlighted) {
self.imageView.tintColor = self.highlightTintColor;
} else {
self.imageView.tintColor = self.tintColor;
}
}
- (void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
[self updateButtonView];
}
- (void)setEnabled:(BOOL)enabled
{
[super setEnabled:enabled];
[self updateButtonView];
}
- (void)setTintColor:(UIColor *)tintColor
{
[super setTintColor:tintColor];
[self updateButtonView];
}
#end
I recommend you to use a custom category to tint your image buttons. Here is a simple implementation that does just that:
UIImage+TintImage.h
#interface UIImage (TintImage)
- (UIImage *)imageTintedWithColor:(UIColor *)tintColor;
#end
UIImage+TintImage.m
#import "UIImage+TintImage.h"
#implementation UIImage (TintImage)
- (UIImage *)imageTintedWithColor:(UIColor *)tintColor
{
if (tintColor == nil) {
tintColor = [UIColor whiteColor];
}
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
// Tint image
[tintColor set];
UIRectFill(rect);
[self drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0f];
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
#end
To use it, just import "UIImage+TintImage.h", then do the following:
UIImage *originalImage = [UIImage imageNamed:#"icn-menu"];
UIImage *tintedImage = [originalImage imageTintedWithColor:[UIColor blueColor]];
UIButton *homeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[homeButton setImage:originalImage forState:UIControlStateNormal];
[homeButton setImage:tintedImage forState:UIControlStateHighlighted];
Swift solution:
Set Default rendering mode on image (in asset catalog or from the code).
Transform the image color with this function:
extension UIImage {
public func transform(withNewColor color: UIColor) -> UIImage
{
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let context = UIGraphicsGetCurrentContext()!
context.translateBy(x: 0, y: size.height)
context.scaleBy(x: 1.0, y: -1.0)
context.setBlendMode(.normal)
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
context.clip(to: rect, mask: cgImage!)
color.setFill()
context.fill(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
Set transformed image to button desired state:
let redImage = image.transform(withNewColor: UIColor.red)
btn?.setImage(redImage, for: .selected)
Thanks to Rufel's answer I was able to fix my issue and reduce the code of my class at the same time:
#import "AppButton.h"
#interface AppButton ()
#property (readonly) UIImage *normalImage;
#end
#implementation AppButton
#synthesize highlightTintColor = _highlightTintColor;
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
[self initialize];
}
return self;
}
- (UIImage *)normalImage
{
return [self imageForState:UIControlStateNormal];
}
- (void)initialize
{
self.adjustsImageWhenHighlighted = NO;
// set disabled image
[self setImage:[self image:self.normalImage tintedWithColor:[UIColor colorWithRGBValue:RGBValueC9]] forState:UIControlStateDisabled];
}
- (void)setHighlightTintColor:(UIColor *)highlightTintColor
{
_highlightTintColor = highlightTintColor;
// update highlighted image
if (highlightTintColor) {
[self setImage:[self image:self.normalImage tintedWithColor:highlightTintColor] forState:UIControlStateHighlighted];
}
}
- (UIImage *)image:(UIImage *)image tintedWithColor:(UIColor *)tintColor
{
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0f);
// Tint image
[tintColor set];
UIRectFill(rect);
[image drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0f];
UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
#end
This is a swift 3 version of rufel Answer ,
extension UIImageView {
fileprivate func tintImage(color: UIColor){
guard let _image = image else { return }
let rect = CGRect(x: 0.0, y: 0.0, width: _image.size.width , height: _image.size.height )
UIGraphicsBeginImageContextWithOptions(_image.size , false, _image.scale)
color.set()
UIRectFill(rect)
_image.draw(in: rect, blendMode: CGBlendMode.destinationIn, alpha: 1.0)
image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
}
OR
extension UIImage {
static func imageTinted(image: UIImage?, color: UIColor) -> UIImage? {
let rect = CGRect(x: 0.0, y: 0.0, width: image?.size.width ?? 0.0, height: image?.size.height ?? 0.0)
UIGraphicsBeginImageContextWithOptions(image?.size ?? CGSize.zero, false, image?.scale ?? 2.0)
color.set()
UIRectFill(rect)
image?.draw(in: rect, blendMode: CGBlendMode.destinationIn, alpha: 1.0)
let tinted = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
return tinted;
}
}

Can I change the border color of an NSTextField?

I want to change the border color of NSTextField object, but I can't achieve it.
I already have tried many solutions EX: be subclass, draw background.
Is there anyone who can resolve this issue?
Use NSBezierPath
- (void)drawRect:(NSRect)dirtyRect
{
NSPoint origin = { 0.0,0.0 };
NSRect rect;
rect.origin = origin;
rect.size.width = [self bounds].size.width;
rect.size.height = [self bounds].size.height;
NSBezierPath * path;
path = [NSBezierPath bezierPathWithRect:rect];
[path setLineWidth:2];
[[NSColor colorWithCalibratedWhite:1.0 alpha:0.394] set];
[path fill];
[[NSColor redColor] set];
[path stroke];
if (([[self window] firstResponder] == [self currentEditor]) && [NSApp isActive])
{
[NSGraphicsContext saveGraphicsState];
NSSetFocusRingStyle(NSFocusRingOnly);
[path fill];
[NSGraphicsContext restoreGraphicsState];
}
else
{
[[self attributedStringValue] drawInRect:rect];
}
}
Output:
You can try CALayer without the need for subclassing
self.wantsLayer = true
self.layer?.borderColor = NSColor.red.cgColor
self.layer?.borderWidth = 1
For me Parag's answer resulted in some strange textfield drawing, so I ended up with this simple code (based on his answer):
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
if (!self.borderColor) {
return;
}
NSPoint origin = { 0.0,0.0 };
NSRect rect;
rect.origin = origin;
rect.size.width = [self bounds].size.width;
rect.size.height = [self bounds].size.height;
NSBezierPath * path;
path = [NSBezierPath bezierPathWithRect:rect];
[path setLineWidth:2];
[self.borderColor set];
[path stroke];
}
SWift 5 version of Parag's answer but adds ability for user to define title and border color.
fileprivate class URLField: NSTextField {
var title : String?
var borderColor: NSColor?
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
if let textEditor = currentEditor() {
textEditor.selectAll(self)
}
}
convenience init(withValue: String?, modalTitle: String?) {
self.init()
if let string = withValue {
self.stringValue = string
}
if let title = modalTitle {
self.title = title
}
self.cell?.controlView?.wantsLayer = true
self.cell?.controlView?.layer?.borderWidth = 1
self.lineBreakMode = .byTruncatingHead
self.usesSingleLineMode = true
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
if let color = borderColor {
///self.layer?.borderColor = color.cgColor
let path = NSBezierPath.init(rect: frame)
path.lineWidth = 1
color.setStroke()
path.stroke()
if self.window?.firstResponder == self.currentEditor() && NSApp.isActive {
NSGraphicsContext.saveGraphicsState()
NSFocusRingPlacement.only.set()
NSGraphicsContext.restoreGraphicsState()
}
}
}
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
if let title = self.title {
self.window?.title = title
}
// MARK: this gets us focus even when modal
self.becomeFirstResponder()
}
}

Recognize a path of user gesture

I'm working on an app with 9 views on screen, and I want the users to connect the views in a way they want, and record their sequence as password.
But I don't know which gesture recognizer I should use.
Should I use CMUnistrokeGestureRecognizer or combination of several swipe gesture or anything else?
Thanks.
You could use a UIPanGestureRecognizer, something like:
CGFloat const kMargin = 10;
- (void)viewDidLoad
{
[super viewDidLoad];
// create a container view that all of our subviews for which we want to detect touches are:
CGFloat containerWidth = fmin(self.view.bounds.size.width, self.view.bounds.size.height) - kMargin * 2.0;
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(kMargin, kMargin, containerWidth, containerWidth)];
container.backgroundColor = [UIColor darkGrayColor];
[self.view addSubview:container];
// now create all of the subviews, specifying a tag for each; and
CGFloat cellWidth = (containerWidth - (4.0 * kMargin)) / 3.0;
for (NSInteger column = 0; column < 3; column++)
{
for (NSInteger row = 0; row < 3; row++)
{
UIView *cell = [[UIView alloc] initWithFrame:CGRectMake(kMargin + column * (cellWidth + kMargin),
kMargin + row * (cellWidth + kMargin),
cellWidth, cellWidth)];
cell.tag = row * 3 + column;
cell.backgroundColor = [UIColor lightGrayColor];
[container addSubview:cell];
}
}
// finally, create the gesture recognizer
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(handlePan:)];
[container addGestureRecognizer:pan];
}
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
static NSMutableArray *gesturedSubviews;
// if we're starting a gesture, initialize our list of subviews that we've gone over
if (gesture.state == UIGestureRecognizerStateBegan)
{
gesturedSubviews = [NSMutableArray array];
}
// now figure out whether:
// (a) are we over a subview; and
// (b) is this a different subview than we last were over
CGPoint location = [gesture locationInView:gesture.view];
for (UIView *subview in gesture.view.subviews)
{
if (CGRectContainsPoint(subview.frame, location))
{
if (subview != [gesturedSubviews lastObject])
{
[gesturedSubviews addObject:subview];
// an example of the sort of graphical flourish to give the
// some visual cue that their going over the subview in question
// was recognized
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionAutoreverse
animations:^{
subview.alpha = 0.5;
}
completion:^(BOOL finished){
subview.alpha = 1.0;
}];
}
}
}
// finally, when done, let's just log the subviews
// you would do whatever you would want here
if (gesture.state == UIGestureRecognizerStateEnded)
{
NSLog(#"We went over:");
for (UIView *subview in gesturedSubviews)
{
NSLog(#" %d", subview.tag);
}
// you might as well clean up your static variable when you're done
gesturedSubviews = nil;
}
}
Obviously, you would create your subviews any way you want, and keep track of them any way you want, but the idea is to have subviews with unique tag numbers, and the gesture recognizer would just see which order you go over them in a single gesture.
Even if I didn't capture precisely what you want, it at least shows you how you can use a pan gesture recognizer to track the movement of your finger from one subview to another.
Update:
If you wanted to draw a path on the screen as the user is signing in, you could create a CAShapeLayer with a UIBezierPath. I'll demonstrate that below, but as a caveat, I feel compelled to point out that this might not be a great security feature: Usually with password entry, you'll show the user enough so that they can confirm that they're doing what they want, but not enough so that someone can glance look over their shoulder and see what the whole password was. When entering a text password, usually iOS momentarily shows you the last key you hit, but quickly turns that into an asterisk so that, at no point, can you see the whole password. Hence my initial suggestion.
But if you really have your heart set on showing the user the path as they draw it, you could use something like the following. First, this requires Quartz 2D. Thus add the QuartzCore.framework to your project (see Linking to a Library or Framework). Second, import the QuartCore headers:
#import <QuartzCore/QuartzCore.h>
Third, replace the pan handler with something like:
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
static NSMutableArray *gesturedSubviews;
static UIBezierPath *path = nil;
static CAShapeLayer *shapeLayer = nil;
// if we're starting a gesture, initialize our list of subviews that we've gone over
if (gesture.state == UIGestureRecognizerStateBegan)
{
gesturedSubviews = [NSMutableArray array];
}
// now figure out whether:
// (a) are we over a subview; and
// (b) is this a different subview than we last were over
CGPoint location = [gesture locationInView:gesture.view];
for (UIView *subview in gesture.view.subviews)
{
if (!path)
{
// if the path hasn't be started, initialize it and the shape layer
path = [UIBezierPath bezierPath];
[path moveToPoint:location];
shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 2.0;
[gesture.view.layer addSublayer:shapeLayer];
}
else
{
// otherwise add this point to the layer's path
[path addLineToPoint:location];
shapeLayer.path = path.CGPath;
}
if (CGRectContainsPoint(subview.frame, location))
{
if (subview != [gesturedSubviews lastObject])
{
[gesturedSubviews addObject:subview];
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionAutoreverse
animations:^{
subview.alpha = 0.5;
}
completion:^(BOOL finished){
subview.alpha = 1.0;
}];
}
}
}
// finally, when done, let's just log the subviews
// you would do whatever you would want here
if (gesture.state == UIGestureRecognizerStateEnded)
{
// assuming the tags are numbers between 0 and 9 (inclusive), we can build the password here
NSMutableString *password = [NSMutableString string];
for (UIView *subview in gesturedSubviews)
[password appendFormat:#"%c", subview.tag + 48];
NSLog(#"Password = %#", password);
// clean up our array of gesturedSubviews
gesturedSubviews = nil;
// clean up the drawing of the path on the screen the user drew
[shapeLayer removeFromSuperlayer];
shapeLayer = nil;
path = nil;
}
}
That yields a path that the user draws as the gesture proceeds:
Rather than showing the path the user draws with each and every movement of the user's finger, maybe you just draw the lines between the center of the subviews, such as:
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
static NSMutableArray *gesturedSubviews;
static UIBezierPath *path = nil;
static CAShapeLayer *shapeLayer = nil;
// if we're starting a gesture, initialize our list of subviews that we've gone over
if (gesture.state == UIGestureRecognizerStateBegan)
{
gesturedSubviews = [NSMutableArray array];
}
// now figure out whether:
// (a) are we over a subview; and
// (b) is this a different subview than we last were over
CGPoint location = [gesture locationInView:gesture.view];
for (UIView *subview in gesture.view.subviews)
{
if (CGRectContainsPoint(subview.frame, location))
{
if (subview != [gesturedSubviews lastObject])
{
[gesturedSubviews addObject:subview];
if (!path)
{
// if the path hasn't be started, initialize it and the shape layer
path = [UIBezierPath bezierPath];
[path moveToPoint:subview.center];
shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 2.0;
[gesture.view.layer addSublayer:shapeLayer];
}
else
{
// otherwise add this point to the layer's path
[path addLineToPoint:subview.center];
shapeLayer.path = path.CGPath;
}
[UIView animateWithDuration:0.25
delay:0.0
options:UIViewAnimationOptionAutoreverse
animations:^{
subview.alpha = 0.5;
}
completion:^(BOOL finished){
subview.alpha = 1.0;
}];
}
}
}
// finally, when done, let's just log the subviews
// you would do whatever you would want here
if (gesture.state == UIGestureRecognizerStateEnded)
{
// assuming the tags are numbers between 0 and 9 (inclusive), we can build the password here
NSMutableString *password = [NSMutableString string];
for (UIView *subview in gesturedSubviews)
[password appendFormat:#"%c", subview.tag + 48];
NSLog(#"Password = %#", password);
// clean up our array of gesturedSubviews
gesturedSubviews = nil;
// clean up the drawing of the path on the screen the user drew
[shapeLayer removeFromSuperlayer];
shapeLayer = nil;
path = nil;
}
}
That yields something like:
You have all sorts of options, but hopefully you now have the building blocks so you can design your own solution.
Forgive me Rob, pure Plagiarism here :) Needed the same code in swift 3.0 :) so I translated this great little example you wrote into swift 3.0.
ViewController.swift
import UIKit
class ViewController: UIViewController {
static let kMargin:CGFloat = 10.0;
override func viewDidLoad()
{
super.viewDidLoad()
// create a container view that all of our subviews for which we want to detect touches are:
let containerWidth = fmin(self.view.bounds.size.width, self.view.bounds.size.height) - ViewController.kMargin * 2.0
let container = UIView(frame: CGRect(x: ViewController.kMargin, y: ViewController.kMargin, width: containerWidth, height: containerWidth))
container.backgroundColor = UIColor.darkGray
view.addSubview(container)
// now create all of the subviews, specifying a tag for each; and
let cellWidth = (containerWidth - (4.0 * ViewController.kMargin)) / 3.0
for column in 0 ..< 3 {
for row in 0 ..< 3 {
let cell = UIView(frame: CGRect(x: ViewController.kMargin + CGFloat(column) * (cellWidth + ViewController.kMargin), y: ViewController.kMargin + CGFloat(row) * (cellWidth + ViewController.kMargin), width: cellWidth, height: cellWidth))
cell.tag = row * 3 + column;
container.addSubview(cell)
}
}
// finally, create the gesture recognizer
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
container.addGestureRecognizer(pan)
}
func handlePan(gesture: UIPanGestureRecognizer)
{
var gesturedSubviews : [UIView] = []
// if we're starting a gesture, initialize our list of subviews that we've gone over
if (gesture.state == .began)
{
gesturedSubviews.removeAll()
}
let location = gesture.location(in: gesture.view)
for subview in (gesture.view?.subviews)! {
if (subview.frame.contains(location)) {
if (subview != gesturedSubviews.last) {
gesturedSubviews.append(subview)
subview.backgroundColor = UIColor.blue
}
}
// finally, when done, let's just log the subviews
// you would do whatever you would want here
if (gesture.state != .recognized)
{
print("We went over:");
for subview in gesturedSubviews {
print(" %d", (subview as AnyObject).tag);
}
// you might as well clean up your static variable when you're done
}
}
}
}
Update: Almost that is; I tried to translate the update too, but my translation missed something and didn't work, so I searched around SO and crafted a similar if slightly different final solution.
import UIKit
import QuartzCore
class ViewController: UIViewController {
static let kMargin:CGFloat = 10.0;
override func viewDidLoad()
{
super.viewDidLoad()
// create a container view that all of our subviews for which we want to detect touches are:
let containerWidth = fmin(self.view.bounds.size.width, self.view.bounds.size.height) - ViewController.kMargin * 2.0
let container = UIView(frame: CGRect(x: ViewController.kMargin, y: ViewController.kMargin, width: containerWidth, height: containerWidth))
container.backgroundColor = UIColor.darkGray
view.addSubview(container)
// now create all of the subviews, specifying a tag for each; and
let cellWidth = (containerWidth - (4.0 * ViewController.kMargin)) / 3.0
for column in 0 ..< 3 {
for row in 0 ..< 3 {
let cell = UIView(frame: CGRect(x: ViewController.kMargin + CGFloat(column) * (cellWidth + ViewController.kMargin), y: ViewController.kMargin + CGFloat(row) * (cellWidth + ViewController.kMargin), width: cellWidth, height: cellWidth))
cell.tag = row * 3 + column;
container.addSubview(cell)
}
}
// finally, create the gesture recognizer
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
container.addGestureRecognizer(pan)
}
// Here's a Swift 3.0 version based on Rajesh Choudhary's answer:
func drawLine(onLayer layer: CALayer, fromPoint start: CGPoint, toPoint end:CGPoint) {
let line = CAShapeLayer()
let linePath = UIBezierPath()
linePath.move(to: start)
linePath.addLine(to: end)
line.path = linePath.cgPath
line.fillColor = nil
line.lineWidth = 8
line.opacity = 0.5
line.strokeColor = UIColor.red.cgColor
layer.addSublayer(line)
}
var gesturedSubviews : [UIView] = []
var startX: CGFloat!
var startY: CGFloat!
var endX: CGFloat!
var endY: CGFloat!
func handlePan(gesture: UIPanGestureRecognizer) {
if (gesture.state == .began)
{
gesturedSubviews = [];
let location = gesture.location(in: gesture.view)
print("location \(location)")
startX = location.x
startY = location.y
}
if (gesture.state == .changed) {
let location = gesture.location(in: gesture.view)
print("location \(location)")
endX = location.x
endY = location.y
drawLine(onLayer: view.layer, fromPoint: CGPoint(x: startX, y: startY), toPoint: CGPoint(x:endX, y:endY))
startX = endX
startY = endY
}
if (gesture.state == .ended) {
let location = gesture.location(in: gesture.view)
print("location \(location)")
drawLine(onLayer: view.layer, fromPoint: CGPoint(x: startX, y: startY), toPoint: CGPoint(x:location.x, y:location.y))
}
// now figure out whether:
// (a) are we over a subview; and
// (b) is this a different subview than we last were over
let location = gesture.location(in: gesture.view)
print("location \(location)")
for subview in (gesture.view?.subviews)! {
if subview.frame.contains(location) {
if (subview != gesturedSubviews.last) {
gesturedSubviews.append(subview)
subview.backgroundColor = UIColor.blue
subview.alpha = 1.0
}
}
}
}
}

IOS: create a UIImage or UIImageView with rounded corners

Is it possible create an UIImage or an UIImageView with rounded corners? Because I want take an UIImage and show it inside an UIImageView, but I don't know how to do it.
Yes, it is possible.
Import the QuartzCore (#import <QuartzCore/QuartzCore.h>) header and play with the layer property of the UIImageView.
yourImageView.layer.cornerRadius = yourRadius;
yourImageView.clipsToBounds = YES;
See the CALayer class reference for more info.
Try this Code For Round Image Import QuartzCore framework
simple way to create Round Image
imageView.layer.backgroundColor=[[UIColor clearColor] CGColor];
imageView.layer.cornerRadius=20;
imageView.layer.borderWidth=2.0;
imageView.layer.masksToBounds = YES;
imageView.layer.borderColor=[[UIColor redColor] CGColor];
Objective-C
-(UIImage *)makeRoundedImage:(UIImage *) image
radius: (float) radius;
{
CALayer *imageLayer = [CALayer layer];
imageLayer.frame = CGRectMake(0, 0, image.size.width, image.size.height);
imageLayer.contents = (id) image.CGImage;
imageLayer.masksToBounds = YES;
imageLayer.cornerRadius = radius;
UIGraphicsBeginImageContext(image.size);
[imageLayer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return roundedImage;
}
Swift 3
func makeRoundedImage(image: UIImage, radius: Float) -> UIImage {
var imageLayer = CALayer()
imageLayer.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
imageLayer.contents = image.cgImage
imageLayer.masksToBounds = true
imageLayer.cornerRadius = radius
UIGraphicsBeginImageContext(image.size)
imageLayer.render(in: UIGraphicsGetCurrentContext())
var roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage
}
uiimageview.layer.cornerRadius = uiimageview.frame.size.height/2;
uiimageview.clipToBounds = YES;
#import <QuartzCore/QuartzCore.h>
// UIImageView+OSExt.h
#import <UIKit/UIKit.h>
#interface UIImageView (OSExt)
- (void)setBorder:(CGFloat)borderWidth color:(UIColor*)color;
#end
// UIImageView+OSExt.m
#import "UIImageView+OSExt.h"
#implementation UIImageView (OSExt)
- (void)layoutSublayersOfLayer:(CALayer *)layer
{
for ( CALayer *sub in layer.sublayers )
{
if ( YES == [sub.name isEqual:#"border-shape"])
{
CGFloat borderHalf = floor([(CAShapeLayer*)sub lineWidth] * .5);
sub.frame = layer.bounds;
[sub setBounds:CGRectInset(layer.bounds, borderHalf, borderHalf)];
[sub setPosition:CGPointMake(CGRectGetMidX(layer.bounds),
CGRectGetMidY(layer.bounds))];
}
}
}
- (void)setBorder:(CGFloat)borderWidth color:(UIColor*)color
{
assert(self.frame.size.width == self.frame.size.height);
for ( CALayer *sub in [NSArray arrayWithArray:self.layer.sublayers] )
{
if ( YES == [sub.name isEqual:#"border-shape"])
{
[sub removeFromSuperlayer];
break;
}
}
CGFloat borderHalf = floor(borderWidth * .5);
self.layer.cornerRadius = self.layer.bounds.size.width * .5;
CAShapeLayer *circleLayer = [CAShapeLayer layer];
self.layer.delegate = (id<CALayerDelegate>)self;
circleLayer.name = #"border-shape";
[circleLayer setBounds:CGRectInset(self.bounds, borderHalf, borderHalf)];
[circleLayer setPosition:CGPointMake(CGRectGetMidX(self.layer.bounds),
CGRectGetMidY(self.layer.bounds))];
[circleLayer setPath:[[UIBezierPath bezierPathWithOvalInRect:circleLayer.bounds] CGPath]];
[circleLayer setStrokeColor:color.CGColor];
[circleLayer setFillColor:[UIColor clearColor].CGColor];
[circleLayer setLineWidth:borderWidth];
{
circleLayer.shadowOffset = CGSizeZero;
circleLayer.shadowColor = [[UIColor whiteColor] CGColor];
circleLayer.shadowRadius = borderWidth;
circleLayer.shadowOpacity = .9f;
circleLayer.shadowOffset = CGSizeZero;
}
// Add the sublayer to the image view's layer tree
[self.layer addSublayer:circleLayer];
// old variant
//CALayer *layer = self.layer;
//layer.masksToBounds = YES;
//layer.cornerRadius = self.frame.size.width * 0.5;
//layer.borderWidth = borderWidth;
//layer.borderColor = color;
}
#end
Setting cornerRadius and clipsToBounds is the right way to do this. However if the view's size changes, the radius will not update. In order to get proper resizing and animation behavior, you need to create a UIImageView subclass.
class RoundImageView: UIImageView {
override var bounds: CGRect {
get {
return super.bounds
}
set {
super.bounds = newValue
setNeedsLayout()
}
}
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = bounds.width / 2.0
clipsToBounds = true
}
}
Try this to get rounded corners of the image View and also to colour the corners:
imageView.layer.cornerRadius = imageView.frame.size.height/2;
imageView.layer.masksToBounds = YES;
imageView.layer.borderColor = [UIColor colorWithRed:148/255. green:79/255. blue:216/255. alpha:1.0].CGColor;
imageView.layer.borderWidth=2;
Condition*: The height and the width of the imageView must be same to get rounded corners.
layer.cornerRadius = imageviewHeight/2
layer.masksToBounds = true
It is possible but I'll advice you to create transparent png image (mask) with round corners and place it over you image with UIImageView. It might be quicker solution (for example if you need animations or scrolling).
Here how i set my rounded avatar at the center of it contain view:
-(void)setRoundedAvatar:(UIImageView *)avatarView toDiameter:(float)newSize atView:(UIView *)containedView;
{
avatarView.layer.cornerRadius = newSize/2;
avatarView.clipsToBounds = YES;
avatarView.frame = CGRectMake(0, 0, newSize, newSize);
CGPoint centerValue = CGPointMake(containView.frame.size.width/2, containedView.frame.size.height/2);
avatarView.center = centerValue;
}
Circle with UIBeizerPath #Swift-3 && #imageExtension
class ViewController: UIViewController {
#IBOutlet weak var imageOutlet: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let image = UIImage(named: "IMG_0001.JPG")
if let image = image {
let renderimage = image.imageCroppingBezierPath(path: UIBezierPath(arcCenter: CGPoint(x:image.size.width/2,y:image.size.width/2 ) , radius: 200, startAngle: 0, endAngle: (2 * CGFloat(M_PI) ), clockwise: true) )
imageOutlet.image = renderimage
}
}
}
extension UIImage {
func imageCroppingBezierPath(path:UIBezierPath) ->UIImage {
let frame = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
//Defining a graphic context to paint on
UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0)
//Get the current graphics context (if it exists)
let context = UIGraphicsGetCurrentContext()
//save the current graphic context
context?.saveGState()
// clipping area
path.addClip()
self.draw(in: frame)
//To extract an image from our canvas
let image = UIGraphicsGetImageFromCurrentImageContext()
//restore graphic context
context?.restoreGState()
//remove current context from stack
UIGraphicsEndImageContext()
return image!
}
}
# import QuartzCore framework
imageView.layer.cornerRadius=imgvwUser.frame.size.width/2;
imageView.layer.masksToBounds = YES;
The height and the width of the imageView must be same to get rounded corners.

UILabel Over UISlider Thumb

How can i put an UILabel over the thumb of UISlider...so that when i move the thumb....UILabel will also move....as it is on the thumb...
Any idea??
Try this
yourLabel = [[UILabel alloc]initWithFrame:....];
//Call this method on Slider value change event
-(void)sliderValueChanged{
CGRect trackRect = [self.slider trackRectForBounds:self.slider.bounds];
CGRect thumbRect = [self.slider thumbRectForBounds:self.slider.bounds
trackRect:trackRect
value:self.slider.value];
yourLabel.center = CGPointMake(thumbRect.origin.x + self.slider.frame.origin.x, self.slider.frame.origin.y - 20);
}
I could get most accurate value by using this snippet.
The "knob" isn't available per public API, so bad chances for hooking it up - if it is a subview at all and not just drawn directly.
So you should add you label to the same view as the slider (make sure you add it later so that appears over it). You can then listen for the value change events and place your label accordingly. It is linear scaling between the endpoints that you need to figure out at first, but it shouldn't be too difficult.
Edit with code:
yourLabel = [[UILabel alloc]initWithFrame:....];
// .. configure label
[[yourSlider superview] addSubview:yourLabel];
[yourSlider addTarget:self action:#selector(adjustLabelForSlider:) forControlEvents:UIControlEventValueChanged];
-(void)adjustLabelForSlider:(id)slider
{
float value = slider.value;
float min = slider.minimumValue;
float max = slider.maximumValue;
CGFloat newX = ...; // Calculate based on yourSlider.frame and value, min, and max
CGFloat newY = ...;
[yourLabel setCenter:CGPointMake(newX,newY)];
}
Note: untested code ;-)
Same answer with swift3:
let trackRect: CGRect = slider.trackRect(forBounds: slider.bounds)
let thumbRect: CGRect = slider.thumbRect(forBounds: slider.bounds , trackRect: trackRect, value: slider.value)
let x = thumbRect.origin.x + slider.frame.origin.x
let y = slider.frame.origin.y - 20
sliderLabel.center = CGPoint(x: x, y: y)
extension UIImage {
class func imageWithLabel(_ label: UILabel) -> UIImage {
UIGraphicsBeginImageContextWithOptions(label.bounds.size, false, 0)
defer { UIGraphicsEndImageContext() }
label.layer.render(in: UIGraphicsGetCurrentContext()!)
return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
}
}
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate, UITextViewDelegate, UIPickerViewDelegate, UIPickerViewDataSource, UIScrollViewDelegate
{
func maskRoundedImage(image: UIImage, radius: CGFloat) -> UIImage {
let imageView: UIImageView = UIImageView(image: image)
let layer = imageView.layer
layer.masksToBounds = true
layer.cornerRadius = radius
UIGraphicsBeginImageContext(imageView.bounds.size)
layer.render(in: UIGraphicsGetCurrentContext()!)
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage!
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 28, height: 28))
label.backgroundColor = .black
label.textAlignment = .center
label.font = label.font.withSize(12)
label.text = String(Int(round( backlightSlider.value * 100 )))
label.textColor = .white
var image = UIImage.imageWithLabel(label)
image = maskRoundedImage(image: image, radius: 14.0)
backlightSlider.setThumbImage(image, for: .normal)
}
Just add an imageview on the thumb of slider add a label on imageview
- (IBAction)valueChangedSlider:(id)sender {
handleView = [_slider.subviews lastObject];
label = [[UILabel alloc] initWithFrame:handleView.bounds];
label = (UILabel*)[handleView viewWithTag:1000];
if (label==nil) {
label = [[UILabel alloc] initWithFrame:handleView.bounds];
label.tag = 1000;
[label setFont:[UIFont systemFontOfSize:12]];
label.textColor = [UIColor redColor];
label.backgroundColor = [UIColor clearColor];
label.textAlignment = NSTextAlignmentCenter;
[handleView addSubview:label];
}
label.text = [NSString stringWithFormat:#"%0.2f", self.slider.value];
}
If anybody is looking for answer in Swift, please take a look of my answer here:- Put Label over UISlider Thumb
It'll work like a charm :)
This could be very helpful...
How to get the center of the thumb image of UISlider