Add shade gradient to the bottom of a uitableviewcell / uiimageview - ios

Does anyone know how add a gradient to the bottom of a uitableviewcell or the uiimageview like the image shown below does?

Add this class into your project (swift):
class UIGradientImageView: UIImageView {
let myGradientLayer: CAGradientLayer
override init?(frame: CGRect){
myGradientLayer = CAGradientLayer()
super.init(frame: frame)
self.setup()
addGradientLayer()
}
func addGradientLayer(){
if myGradientLayer.superlayer == nil{
self.layer.addSublayer(myGradientLayer)
}
}
required init(coder aDecoder: NSCoder){
myGradientLayer = CAGradientLayer()
super.init(coder: aDecoder)
self.setup()
addGradientLayer()
}
func getColors() -> [CGColorRef] {
return [UIColor.clearColor().CGColor, UIColor(red: 0, green: 0, blue: 0, alpha: 0.5).CGColor]
}
func getLocations() -> [CGFloat]{
return [0.5, 0.9]
}
func setup() {
myGradientLayer.startPoint = CGPoint(x: 0.5, y: 0)
myGradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
let colors = getColors()
myGradientLayer.colors = colors
myGradientLayer.opaque = false
myGradientLayer.locations = getLocations()
}
override func layoutSubviews() {
super.layoutSubviews()
myGradientLayer.frame = self.layer.bounds
}
}
UPDATE: Objective-C translated solution. Credits to #SleepsOnNewspapers.
#import "UIGradientImageView.h"
#interface UIGradientImageView()
#property (nonatomic, strong) CAGradientLayer *myGradientLayer;
#end
#implementation UIGradientImageView
-(instancetype)initWithFrame:(CGRect)frame{
if(self){
self = [super initWithFrame:frame];
self.myGradientLayer = [[CAGradientLayer alloc]init];
[self setup];
[self addGradientLayer];
}
return self;
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
if(self){
self = [super initWithCoder:aDecoder];
self.myGradientLayer = [[CAGradientLayer alloc]init];
[self setup];
[self addGradientLayer];
}
return self;
}
-(void)addGradientLayer{
if (self.myGradientLayer.superlayer == nil) {
[self.layer addSublayer:self.myGradientLayer];
}
}
Make your UIImageView class on your storyboard to be this one instead of default.

Related

Gradient Layer Is Not Working on Portrait Mode in Xib File

i wants to set gradient layer on a UIView in Xib File but in Portrait Mode Gradient Layer it's not fill all Of UIView.
Gradient Class :
extension UIView {
#discardableResult
func applyGradient(colours: [UIColor]) -> CAGradientLayer {
return self.applyGradient(colours: colours, locations: nil)
}
#discardableResult
func applyGradient(colours: [UIColor], locations: [NSNumber]?) -> CAGradientLayer {
let gradient: CAGradientLayer = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = colours.map { $0.cgColor }
gradient.locations = locations
self.layer.insertSublayer(gradient, at: 0)
return gradient
}
}
Xib File Class :
class TempView: UIView {
override func awakeFromNib() {
super.awakeFromNib()
}
override func layoutSubviews() {
super.layoutSubviews();
self.viewMain.applyGradient(colours: [.black, .gray])
}
}
Try this:
class TempView: UIView {
override func awakeFromNib() {
super.awakeFromNib()
}
override func layoutSubviews() {
super.layoutSubviews();
self.viewMain.layoutIfNeeded()
self.viewMain.applyGradient(colours: [.black, .gray])
}
}
i used this extension To solve This Problem :
extension UIView {
func addGradient(colors: [UIColor], locations: [NSNumber]) {
addSubview(ViewWithGradient(addTo: self, colors: colors, locations: locations))
}
}
class ViewWithGradient: UIView {
private var gradient = CAGradientLayer()
init(addTo parentView: UIView, colors: [UIColor], locations: [NSNumber]){
super.init(frame: CGRect(x: 0, y: 0, width: 1, height: 2))
restorationIdentifier = "__ViewWithGradient"
for subView in parentView.subviews {
if let subView = subView as? ViewWithGradient {
if subView.restorationIdentifier == restorationIdentifier {
subView.removeFromSuperview()
break
}
}
}
let cgColors = colors.map { (color) -> CGColor in
return color.cgColor
}
gradient.frame = parentView.frame
gradient.colors = cgColors
gradient.locations = locations
backgroundColor = .clear
parentView.addSubview(self)
parentView.layer.insertSublayer(gradient, at: 0)
parentView.backgroundColor = .clear
autoresizingMask = [.flexibleWidth, .flexibleHeight]
clipsToBounds = true
parentView.layer.masksToBounds = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
if let parentView = superview {
gradient.frame = parentView.bounds
}
}
override func removeFromSuperview() {
super.removeFromSuperview()
gradient.removeFromSuperlayer()
}
}

Setting the frame of a sublayer inside of layoutSubviews() makes changing it impossible. Any workaround for that?

I'm working on a customizable UITextField (see code below). I have added a border at the bottom (you can set it in the storyboard). However, I had problems setting the frame of the CALayer that this border consists of.
If I set it inside the didSet method of var showBottomBorder it doesn't appear on the screen. I think this is because the frame (of the UITextField) hasn't been calculated yet (maybe didSet gets called before that).
So I moved it to the layoutSubviews() method (see code below). This works perfectly.
But now I have another problem. I can't really change that frame anymore. Every time I change it, it gets reset by layoutSubviews() which I think is called then.
At the bottom of my code, there is the method textFieldDidBeginEditing. In there, I wanted to move up my bottom border (animated). But it doesn't work. The border does not move anywhere. And like I said, I think it's because I set the frame inside the layoutSubviews() method.
Is there a better way to set the frame of the bottom border? A way which allows me to change stuff?
#IBDesignable
class CustomizableTextField: UITextField, UITextFieldDelegate {
// MARK: - Properties
private var bottomBorder = CALayer()
// MARK: - #IBInspectables
#IBInspectable var roundCorners: CGFloat = 0 {
didSet {
self.layer.cornerRadius = roundCorners
self.clipsToBounds = true
}
}
/** -- */
#IBInspectable var borderWidth: CGFloat = 1.0 {
didSet {
self.layer.borderWidth = self.borderWidth
}
}
#IBInspectable var borderColor: UIColor = UIColor.white {
didSet {
self.layer.borderColor = self.borderColor.cgColor
}
}
/** -- */
/** -- */
private var showBottomBorder: Bool = false {
didSet {
switch showBottomBorder {
case true:
bottomBorder.borderColor = self.bottomBorderColor.cgColor
bottomBorder.borderWidth = self.bottomBorderWidth
self.layer.addSublayer(bottomBorder)
self.layer.masksToBounds = true
break
case false:
bottomBorder.removeFromSuperlayer()
break
}
}
}
#IBInspectable var bottomBorderWidth: CGFloat = 1.0 {
didSet {
self.showBottomBorder = false
self.showBottomBorder = true
}
}
#IBInspectable var bottomBorderColor: UIColor = UIColor.white {
didSet {
self.showBottomBorder = false
self.showBottomBorder = true
}
}
/** -- */
/** -- */
// Somwhow, the default panel for my font color doesn't change anything, so I created this
#IBInspectable var fixedFontColor: UIColor = UIColor.white {
didSet {
self.textColor = fixedFontColor
}
}
#IBInspectable var placeholderFontColor: UIColor = UIColor.white {
didSet {
var placeholderTxt = ""
if let txt = self.placeholder {
placeholderTxt = txt
}
self.attributedPlaceholder = NSAttributedString(string: placeholderTxt, attributes: [NSForegroundColorAttributeName: placeholderFontColor])
}
}
/** -- */
// MARK: - Overrides and Initializers
override init(frame: CGRect) {
super.init(frame: frame)
}
override func layoutSubviews() {
super.layoutSubviews()
// HERE
bottomBorder.frame = CGRect(x: 0, y: self.frame.size.height - self.bottomBorderWidth, width: self.frame.size.width, height: self.frame.size.height)
}
// setting the textField delegate to self
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//self.borderStyle = .none
self.delegate = self
}
// MARK: - Events
func textFieldDidBeginEditing(_ textField: UITextField) {
}
You can use and extension on UITextFiled for setting the border.
And keep a reference to it with KVC.
By overriding LayoutSubview, every the layout will change, we'l check if the border exists, if so remove it, and re-create a new one with the new frame:
import UIKit
let MyTopBorder = "myTopBorder"
let MyBottomBorder = "myBottomBorder"
struct Defaults {
static let width = CGFloat(1.0)
static func bottonBorderFrame(view: UIView)->CGRect {
return CGRect(x: CGFloat(0), y: view.frame.size.height - Defaults.width, width: view.frame.size.width, height: view.frame.size.height)
}
static func topBorderFrame(view: UIView)->CGRect {
return CGRect(x: CGFloat(0), y: CGFloat(0) , width: view.frame.size.width, height: Defaults.width)
}
}
extension UITextField
{
func setBottomBorder(color:CGColor)
{
if let isBottomBorder = self.getBottomBorderIfExists() {
isBottomBorder.removeFromSuperlayer()
}
self.setBorderWithFrame(Defaults.bottonBorderFrame(self), color: color, andKey: MyBottomBorder)
}
func setTopBorder(color:CGColor)
{
if let isTopBorder = self.getTopBorderIfExists() {
isTopBorder.removeFromSuperlayer()
}
self.setBorderWithFrame(Defaults.topBorderFrame(self), color: color, andKey: MyTopBorder)
}
func setBorderWithFrame(frame: CGRect, color: CGColor, andKey: String) {
self.borderStyle = UITextBorderStyle.None;
let border = CALayer()
border.borderColor = color
border.frame = frame
border.borderWidth = Defaults.width
self.layer.addSublayer(border)
self.layer.masksToBounds = true
self.layer.setValue(border, forKey: andKey)
}
func removeTopBorder() {
if let isTopBorder = self.getTopBorderIfExists() {
self.layer.setValue(nil, forKey: MyTopBorder)
isTopBorder.removeFromSuperlayer()
}
}
func removeBottomBorder() {
if let isBottomBorder = self.getBottomBorderIfExists() {
self.layer.setValue(nil, forKey: MyBottomBorder)
isBottomBorder.removeFromSuperlayer()
}
}
private func getBorderIfExistsByKey(key: String)->CALayer? {
if let isBorderSet = self.layer.valueForKey(key) {
if let borderIsCALayer = isBorderSet as? CALayer {
return borderIsCALayer
}
}
return nil
}
private func getTopBorderIfExists()->CALayer? {
return self.getBorderIfExistsByKey(MyTopBorder)
}
private func getBottomBorderIfExists()->CALayer? {
return self.getBorderIfExistsByKey(MyBottomBorder)
}
public override func layoutSubviews() {
super.layoutSubviews()
// Update bottom on frame change
if let isBottomBorder = self.getBottomBorderIfExists() {
let borderColor = isBottomBorder .borderColor
self.removeBottomBorder()
self.setBottomBorder(borderColor!)
}
// Update top on frame change
if let isTopBorder = self.getTopBorderIfExists() {
let borderColor = isTopBorder.borderColor
self.removeTopBorder()
self.setTopBorder(borderColor!)
}
}
}
Usage:
let textField = UITextField(frame: CGRect(x: 100,y: 100, width: 100, height: 100))
textField.backgroundColor = UIColor.blueColor() // Thie color is for visulizing better
self.view.addSubview(textField)
textField.setBottomBorder(UIColor.blackColor().CGColor) // Now you have a border
textField.frame = CGRect(x: 150, y: 200, width: 200, height: 200) // And the border updated to the new frame
// Now if you would like to change from bottom to top, simply do this:
textField.removeBottomBorder()
textField.setTopBorder(UIColor.blackColor().CGColor)

ChildViewController and IBOutlet UIView in SWIFT

I spent days to try to solve this problem but can't find any solution (except programmaticaly):
I have 2 UIViewController where the 2nd is a UIChildViewController.
In the ChildViewController I have an IBOutlet UIView class's attribute linked to a CustomClassUIview in the storyboard.
This CustomClassUIview have methods and attribute to update the shape layer define inside this class.
The problem is when I try to access to one attribute of this custom uiview, it returns nil.
I know that the IBOutlet is fired outside viewDidLoad, viewWillAppear,... but I don't know how to keep alloc.
I did with storyboard to be easier to design but if I did this only programmaticaly it works.
Any help please.
class ChildViewController: UIViewController {
#IBOutlet weak var customUIView: CustomClassUIView!
var upperValueProgress:CGFloat = 0 {
didSet {
self.customUIView.progress = upperValueProgress
updateLayerFrames()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.greenColor()
}
override func viewWillAppear(animated: Bool) {
customUIView.progress = 0
}
func updateLayerFrames() {
customUIView.reveal()
}
}
class FirstViewController: UIViewController {
var customUIViewController:ChildViewController!
override func viewDidLoad() {
super.viewDidLoad()
self.customUIViewController = ChildViewController()
}
...
func update(){
self.customUIViewController.upperValueProgress = 56 // Example
}
,;
#IBDesignable class CustomClassUIview: UIView {
let pathLayer = CAShapeLayer()
var circleRadius: CGFloat {
get {
return self.frame.width / 2
}
set {
}
}
#IBInspectable var progress: CGFloat = 0 {
didSet {
reveal()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
reveal()
}
func configure() {
pathLayer.frame = self.bounds
pathLayer.lineWidth = 2
pathLayer.backgroundColor = UIColor.clearColor().CGColor
pathLayer.fillColor = UIColor.clearColor().CGColor
pathLayer.strokeColor = UIColor.redColor().CGColor
layer.addSublayer(segmentTimerPathLayer)
backgroundColor = UIColor.whiteColor()
progress = 0
}
func circleFrame() -> CGRect {
var circleFrame = CGRect(x: self.bounds.minX, y: self.bounds.minY, width: 2*circleRadius, height: 2*circleRadius)
circleFrame.origin.x = 0
circleFrame.origin.y = 0
return circleFrame
}
func circlePath() -> UIBezierPath {
return UIBezierPath(ovalInRect: circleFrame())
}
override func layoutSubviews() {
super.layoutSubviews()
pathLayer.frame = bounds
pathLayer.path = circlePath().CGPath
}
func reveal() {
println(progress)
}
}

iOS - Custom Confirmation view

I am working on creating a custom control once user presses a button and action completes. I'm trying to replicate behavior of apple music app when album is added it shows confirmation view in center with a check mark as shown below. Are there any similar cocoa controls available to use?
(swift)
Create a singleton class
class CustomView: UIView {
class var sharedView : CustomView {
struct Static {
static var instance : CustomView?
static var token : dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = CustomView()
}
return Static.instance!
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func showInView(view:UIWindow) {
var image = UIImage(named:"SomeImage")
self.frame = view.frame
var originX = view.center.x
var originY = view.center.y
let centerView = UIImageView()
centerView.center = CGPointMake(originX, originY)
centerView.contentMode = UIViewContentMode.Center
centerView.image = image
centerView.alpha = 0
self.addSubview(centerView)
view.addSubview(self)
UIView.animateWithDuration(1, animations: { () -> Void in
centerView.alpha = 1
}) { (_) -> Void in
UIView.animateWithDuration(1, animations: { () -> Void in
centerView.frame.size = CGSizeMake(0,0)
centerView.alpha = 0
}) { (_) -> Void in
self.hide()
}
}
}
func hide()
{
if self.superview != nil
{
self.removeFromSuperview()
}
}
}
In your viewController you can just call the method CustomView.sharedView.showInView(view:UIApplication.sharedApplication.keyWindow())
Objective c .h
#import <UIKit/UIKit.h>
#interface CustomView : UIView
+ (instancetype)sharedInstance;
-(void)showInView:(UIWindow*)view;
#end
objective c .m
#import "CustomView.h"
#implementation CustomView
+ (instancetype)sharedInstance
{
static CustomView *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[CustomView alloc] init];
});
return sharedInstance;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(void)showInView:(UIWindow*)view {
UIImage *image = [UIImage imageNamed:#"img.png"];
self.frame = view.frame;
CGFloat originX = view.center.x;
CGFloat originY = view.center.y;
UIImageView *centerView = [UIImageView new];
centerView.center = CGPointMake(originX, originY);
centerView.contentMode = UIViewContentModeCenter;
centerView.image = image;
centerView.alpha = 0;
[self addSubview:centerView];
[view addSubview:self];
[UIView animateWithDuration:1 animations:^{
centerView.alpha = 1;
} completion:^(BOOL finished) {
[UIView animateWithDuration:1 animations:^{
centerView.frame = CGRectMake(originX, originY, 0, 0);
centerView.alpha = 0;
} completion:^(BOOL finished) {
[self hideView];
}];
}];
}
-(void)hideView {
if(self.superview) {
[self removeFromSuperview];
}
}
#end
Import CustomView.h in your file and
[[CustomView sharedInstance] showInView:[[UIApplication sharedApplication]keyWindow]];

Loading a xib from a view subclass in Swift

I’m trying to reuse a component from a xib in a Swift project, but I’m using Objective C logic. Thus, subclassing a view and loading the xib, then instantiating the custom view in the view controller.
Translating the code into Swift is not producing the results expected.
CustomView.swift:
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("Error detected")
self.commonInit()
}
private func commonInit() {
NSBundle.mainBundle().loadNibNamed("mainBar", owner: self, options: nil)
self.addSubview(self)
}
viewController.swift:
var bottomBar : customView = customView(frame: CGRect(x: 0, y: 250, width: 250, height: 70))
//bottomBar.backgroundColor = UIColor.redColor()
self.view.addSubview(bottomBar)
Objective-C I used as a reference:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self baseInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[[NSBundle mainBundle] loadNibNamed:#"yourXib" owner:self options:nil];
[self addSubview:self.view];
}
return self;
}
CustomView * myView = [CustomView alloc]init];
[self.view addSubView:myView];
Any comment in the right direction is appreciated.
Not 100 percent sure if your OBJ-C code actually work, but most probably don't.
You have to realise that this [[NSBundle mainBundle] loadNibNamed:#"yourXib" owner:self options:nil]; returns always array of objects in your IB xib files. You actually don't assign this object anywhere as far as I see.
The problem with this implementation might be frame that can be taken from bounds.
This code works if we assume that there is only one object in an IB object.
private func commonInit()
{
var nibView = NSBundle.mainBundle().loadNibNamed("mainBar", owner: self, options: nil)[0] as UIView
nibView.frame = self.bounds;
self.addSubview(nibView)
}
You can try This
override init(frame: CGRect) {
super.init(frame: frame)
loadViewFromNib ()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadViewFromNib ()
}
func loadViewFromNib() {
let view = UINib(nibName: "CreditCardExperyView", bundle: NSBundle(forClass: self.dynamicType)).instantiateWithOwner(self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.addSubview(view);
}
// Call subview
let creditCardView : CreditCardExperyView = CreditCardExperyView(frame: CGRect(x: 0, y: UIScreen.mainScreen().bounds.size.height - 280, width: UIScreen.mainScreen().bounds.size.width, height: 280))
selfView.addSubview(creditCardView)

Resources