Starting from the right end, I have to fill the color in UITableViewCell (or to say it better, change the background of UITableViewCell starting from right to left in a small duration). How may I achieve this ?
It's quite easy.
Just put a view in the background, starting with a 0 width and on the right of the tableview/tableview cell, then animate it to the full width of the superview.
Code snippet (in form of a Swift playground)
import UIKit
import XCPlayground
let tableView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
tableView.backgroundColor = UIColor.whiteColor()
var animatedBackroundColorView = UIView(frame: CGRect(x: tableView.frame.width, y: tableView.frame.origin.y, width: tableView.frame.width, height: tableView.frame.height))
animatedBackroundColorView.backgroundColor = UIColor.orangeColor()
tableView.addSubview(animatedBackroundColorView)
UIView.animateWithDuration(1) { () -> Void in
animatedBackroundColorView.frame = tableView.frame
}
XCPShowView("identifier", view: tableView)
Rending:
try this
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(cell.frame.size.width + 1,0,cell.frame.size.width,cell.frame.size.height)];
[backgroundView setBackgroundColor:yourColor];
[cell addSubview:backgroundView];
[UIView animateWithDuration:2.0 animations:^{
CGRect rect = backgroundView.frame;
rect.origin.x = 0;
[backgroundView setFrame:rect];
} completion:NULL];
Related
I'm implementing a "fake" border using a view with a background color so that the border doesn't cover another view. (as per this answer and the following code)
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
backgroundView.backgroundColor = [UIColor blackColor]; /* I want this to be clear except for the part outside bView */
backgroundView.clipsToBounds = NO;
UIView *bView = [[UIView alloc] initWithFrame:CGRectInset(backgroundView.bounds, 3, 3)];
bView.backgroundColor = [UIColor redColor]; /* I want this to be clear */
UIView *cView = [[UIView alloc] initWithFrame:CGRectMake(-50, -50, 100, 100)];
cView.backgroundColor = [UIColor yellowColor];
[bView addSubview:cView];
[backgroundView addSubview:bView];
[self.window addSubview:backgroundView];
How can I have backgroundView be clear(transparent) except for the border and bview be completely transparent? If I set both color to clear, I will lose my border. I am using swift unlike the code example.
Thanks
This is what I actually want. The big box needs to be transparent except for the fake black border around it so that the text (and everything behind it) can show up.
You say you want this:
So now I will easily construct it, but I will make the third view white so we can see it (with an annotation that it should be clear):
self.view.backgroundColor = .gray
let borderView = UIView(frame:CGRect(x: 150, y: 150, width: 200, height: 200))
borderView.backgroundColor = .clear
borderView.layer.borderWidth = 3
self.view.addSubview(borderView)
let yellowView = UIView(frame:CGRect(x: 100, y: 100, width: 100, height: 100))
yellowView.backgroundColor = .yellow
self.view.addSubview(yellowView)
let clearView = UIView(frame:borderView.frame.insetBy(dx: 3, dy: 3))
clearView.backgroundColor = .white // should be .clear
self.view.addSubview(clearView)
Result:
Substitute .clear for .white to get the desired outcome.
Well, I can't give you definitive performance data, but I would expect this to give you better performance than multiple views to create a "fake" border...
class myView: UIView {
override func draw(_ rect: CGRect) {
UIColor.black.set()
let context = UIGraphicsGetCurrentContext()
context?.stroke(rect.insetBy(dx: 1.5, dy: 1.5), width: 3.0)
}
}
let backgroundView = myView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
backgroundView.backgroundColor = UIColor.clear
let cView = UIView(frame: CGRect(x: -50, y: -50, width: 100, height: 100))
cView.backgroundColor = UIColor.yellow
backgroundView.addSubview(cView)
self.view.addSubview(backgroundView)
Of course, if you really want to do this with subviews to create the frame, this will also do the job. It adds 4 subviews to the background view, one for each side of the rectangle:
let backgroundView = UIView(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
backgroundView.backgroundColor = UIColor.clear
backgroundView.clipsToBounds = false
let bgvFrame = backgroundView.bounds
let leftEdge = UIView(frame: CGRect(x: 0, y: 0, width: 3, height: bgvFrame.size.height))
leftEdge.backgroundColor = UIColor.black
let topEdge = UIView(frame: CGRect(x: 0, y: 0, width: bgvFrame.size.width, height: 3))
topEdge.backgroundColor = UIColor.black
let rightEdge = UIView(frame: CGRect(x: bgvFrame.size.width - 3, y: 0, width: 3, height: bgvFrame.size.height))
rightEdge.backgroundColor = UIColor.black
let bottomEdge = UIView(frame: CGRect(x: 0, y: bgvFrame.size.height - 3, width: bgvFrame.size.width, height: 3))
bottomEdge.backgroundColor = UIColor.black
backgroundView.addSubview(leftEdge)
backgroundView.addSubview(topEdge)
backgroundView.addSubview(rightEdge)
backgroundView.addSubview(bottomEdge)
let cView = UIView(frame: CGRect(x: -50, y: -50, width: 100, height: 100))
cView.backgroundColor = UIColor.yellow
backgroundView.addSubview(cView)
self.view.addSubview(backgroundView)
I have a UIView that holds a UILabel inside.
After applying affine transform on the UIView using:
myView.transform = CGAffineTransformMakeScale(4, 4);
My UILabel (which is a sub view to myView) grows as well.
Is there a way to prevent this?
i tried:
1) Using the CGAffineTransformIdentity flag on the label.
2) Adding a superview to myView and adding myView as superview's subview, and the label as a subview to the superview (and not myView).
Non of them seem to be working, the label keeps growing.
Any ideas?
You answered your own question with option 2. Not sure why it's not working since you did not supply any code. The playground code below shows it will work. Uncomment out the last line to transform the subview but not the label.
import UIKit
import XCPlayground
let superview = UIView(frame: CGRect(x: 10, y: 10, width: 200, height: 200))
XCPlaygroundPage.currentPage.liveView = superview
superview.backgroundColor = UIColor.redColor()
let view = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
view.backgroundColor = UIColor.greenColor()
superview.addSubview(view)
let label = UILabel(frame: CGRect(x: 20, y: 10, width: 40, height: 40))
label.text = "Hello"
superview.addSubview(label)
//view.transform = CGAffineTransformMakeScale(2, 2)
I have the following problem, I have created view1 above view2
Then added scrollview to view1 , the scrollview contains some ui elements added by code
The problem that my scrollview is not scrolling, and the content of it appear above view 2 while I need it to be behind it and to be able to scroll it
screenshot is following
my code of adding UI elements is following
mainScrollView = UIScrollView(frame: CGRectMake(0, 0, self.view.frame.size.width, 1000))
let awesomeView2: UIImageView = UIImageView(frame: CGRectMake(0, 0, self.view.frame.size.width, 180))
awesomeView2.load(selectedItem.listingImage);
mainScrollView.addSubview(awesomeView2);
let title: UILabel = UILabel(frame: CGRectMake(0, 180, self.view.frame.size.width, 44));
title.text = selectedItem.listingTitle;
title.textAlignment = .Right;
mainScrollView.addSubview(title);
let catName: UILabel = UILabel(frame: CGRectMake(200, 224, 120, 21));
catName.text = selectedItem.catName
catName.textAlignment = .Right;
mainScrollView.addSubview(catName);
let datePublished: UILabel = UILabel(frame: CGRectMake(0, 224, 200, 21));
datePublished.text = selectedItem.dateCreated
datePublished.textAlignment = .Left
mainScrollView.addSubview(datePublished);
let grayColor = UIColor(red: 1, green: 165/255, blue: 0, alpha: 1)
let iconsView: UIView = UIView(frame: CGRectMake(0, 250, self.view.frame.size.width, 32));
iconsView.backgroundColor = grayColor
mainScrollView.addSubview(iconsView);
if (KhawaterDataManager.instance.banners.itemBanners.count > 0) {
let awesomeView3: UIImageView = UIImageView(frame: CGRectMake(0, 282, self.view.frame.size.width, 50))
awesomeView3.load(KhawaterDataManager.instance.banners.itemBanners[0].image);
mainScrollView.addSubview(awesomeView3);
}
// test
let catName1: UILabel = UILabel(frame: CGRectMake(0, 335, self.view.frame.size.width, 50));
catName1.text = selectedItem.catName
mainScrollView.addSubview(catName1);
let catName2: UILabel = UILabel(frame: CGRectMake(0, 385, self.view.frame.size.width, 50));
catName2.text = selectedItem.catName
mainScrollView.addSubview(catName2);
let catName3: UILabel = UILabel(frame: CGRectMake(0, 435, self.view.frame.size.width, 50));
catName3.text = selectedItem.catName
mainScrollView.addSubview(catName3);
mainScrollView.contentSize = CGSizeMake(self.view.frame.size.width, 1000)
mainScrollView.scrollEnabled = true;
mainView.addSubview(mainScrollView);
please anyone tell me what is the problem here ?
Please check this line
mainScrollView.contentSize = CGSizeMake(self.view.frame.size.width, 1000)
Why content height 1000. This is too much. This should be
mainScrollView.contentSize = CGSizeMake(self.view.frame.size.width, 435+50)
And another issue is that why set your scollView as sub view of mainView rather than self.view.
Also you might want to add all subviews (awesomeView2, title, catName, etc)
to container view, and then add container view to scroll view.
something like:
let containerView: UIView = {
let view = UIView()
view.addSubview(self.awesomeView2)
view.addSubview(self.title)
view.addSubview(self.catName)
view.addSubview(self.datePublished)
return view
}()
mainScrollView.addSubview(containerView)
I am setting up a UIImageView as a leftView on a UITextField like so:
UIImageView *envelopeView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.height*.1, self.height*.1)];
envelopeView.image = [UIImage imageNamed:#"envelope.png"];
envelopeView.contentMode = UIViewContentModeScaleAspectFit;
envelopeView.bounds = CGRectInset(envelopeView.frame, 15, 10);
self.emailAddress.leftView = envelopeView;
self.emailAddress.leftViewMode = UITextFieldViewModeAlways;
which gets me the following:
As you can see the left size of the image goes right up to the left edge of the button even though I tried to set an inset. How can I move this envelope in so that it's got padding on all sides?
Update: I tried the proposed answer of changing the UIImageView frame like so, but the envelope is still lined up on the left side at the border of the UITextField:
CGFloat padding = 20;
UIImageView *envelopeView = [[UIImageView alloc] initWithFrame:CGRectMake(3*padding, padding, self.height*.1-padding, self.height*.1-padding)];
For Swift 3 Users
Here is what worked for me:
extension UITextField {
/// set icon of 20x20 with left padding of 8px
func setLeftIcon(_ icon: UIImage) {
let padding = 8
let size = 20
let outerView = UIView(frame: CGRect(x: 0, y: 0, width: size+padding, height: size) )
let iconView = UIImageView(frame: CGRect(x: padding, y: 0, width: size, height: size))
iconView.image = icon
outerView.addSubview(iconView)
leftView = outerView
leftViewMode = .always
}
}
test:
txOrigin.setLeftIcon(icon_location)
result:
For Swift 4.2 +
You can use this extension:
extension UITextField {
func leftImage(_ image: UIImage?, imageWidth: CGFloat, padding: CGFloat) {
let imageView = UIImageView(image: image)
imageView.frame = CGRect(x: padding, y: 0, width: imageWidth, height: frame.height)
imageView.contentMode = .center
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: imageWidth + 2 * padding, height: frame.height))
containerView.addSubview(imageView)
leftView = containerView
leftViewMode = .always
}
}
you can simply try this:
UIImageView *envelopeView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 0, 30, 30)];
envelopeView.image = [UIImage imageNamed:#"comment-128.png"];
envelopeView.contentMode = UIViewContentModeScaleAspectFit;
UIView *test= [[UIView alloc]initWithFrame:CGRectMake(20, 0, 30, 30)];
[test addSubview:envelopeView];
[self.textField.leftView setFrame:envelopeView.frame];
self.textField.leftView =test;
self.textField.leftViewMode = UITextFieldViewModeAlways;
You can use this. Change your frame according to your need.
NSTextAttachment* placeholderImageTextAttachment = [[NSTextAttachment alloc] init];
placeholderImageTextAttachment.image = [UIImage imageNamed:#"Search"];
placeholderImageTextAttachment.bounds = CGRectMake(0, -2, 16, 16);
NSMutableAttributedString* placeholderImageString = [[NSAttributedString attributedStringWithAttachment:placeholderImageTextAttachment] mutableCopy];
NSMutableAttributedString* placeholderString = [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(#" Search", nil)];
[placeholderImageString appendAttributedString:placeholderString];
_txtFieldSearch.attributedPlaceholder = placeholderImageString;
_txtFieldSearch.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
Tweetbot and Kickstarter for iOS uses a cool feature on user profiles that have a banner image. If you pull down on the tableView the image zooms.
I have it partially working using the following, it changes the height of the image, but strangely, not the width:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
UIImageView *imageView = (UIImageView *)self.tableView.tableHeaderView;
CGFloat y = -scrollView.contentOffset.y;
imageView.frame = CGRectMake(0, scrollView.contentOffset.y, self.cachedImageViewSize.size.width+y, self.cachedImageViewSize.size.height+y);
NSLog(#"%#", NSStringFromCGRect(imageView.frame));
}
Does anyone know how to recreate this effect?
Ok, I figured it out. Here is what I did:
- (void)viewDidLoad {
[super viewDidLoad];
self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"church-welcome.png"]];
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.cachedImageViewSize = self.imageView.frame;
[self.tableView addSubview:self.imageView];
[self.tableView sendSubviewToBack:self.imageView];
self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 170)];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat y = -scrollView.contentOffset.y;
if (y > 0) {
self.imageView.frame = CGRectMake(0, scrollView.contentOffset.y, self.cachedImageViewSize.size.width+y, self.cachedImageViewSize.size.height+y);
self.imageView.center = CGPointMake(self.view.center.x, self.imageView.center.y);
}
}
The above answer for Swift:
Variable Decelerations:
var imageView: UIImageView!
var cachedImageViewSize: CGRect!
viewDidLoad:
var imageView: UIImageView!
var cachedImageViewSize: CGRect!
override func viewDidLoad() {
super.viewDidLoad()
self.imageView = UIImageView(image: UIImage(named: "image-plane"))
imageView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 170)
self.imageView.contentMode = .ScaleAspectFill
self.cachedImageViewSize = self.imageView.frame
self.tableView.addSubview(self.imageView)
self.imageView.center = CGPointMake(self.view.center.x, self.imageView.center.y)
self.tableView.sendSubviewToBack(self.imageView)
self.tableView.tableHeaderView = UIView(frame: CGRectMake(0, 0, self.view.frame.size.width, 170))
}
scrollViewDidScroll:
override func scrollViewDidScroll(scrollView: UIScrollView) {
let y: CGFloat = -scrollView.contentOffset.y
if y > 0 {
self.imageView.frame = CGRectMake(0, scrollView.contentOffset.y, self.cachedImageViewSize.size.width + y, self.cachedImageViewSize.size.height + y)
self.imageView.center = CGPointMake(self.view.center.x, self.imageView.center.y)
}
}
Swift 3.0 version of Nic Hubbard's answer:
override func viewDidLoad() {
self.imageView = UIImageView(image: UIImage(named: "header-image"))
imageView.frame = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 170)
self.imageView.contentMode = .scaleAspectFill
self.cachedImageViewSize = self.imageView.frame
self.tableView.addSubview(self.imageView)
self.imageView.center = CGPoint(x: self.view.center.x, y:self.imageView.center.y)
self.tableView.sendSubview(toBack: self.imageView)
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 170))
}
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
let y: CGFloat = -scrollView.contentOffset.y
if y > 0 {
self.imageView.frame = CGRect(x: 0, y: scrollView.contentOffset.y, width: self.cachedImageViewSize.size.width + y, height: self.cachedImageViewSize.size.height + y)
self.imageView.center = CGPoint(x: self.view.center.x, y: self.imageView.center.y)
}
}
I think it's simply resize the image view to fit the vertial space, while keeping the same aspect ratio.
Swift 2 version of Nic Hubbard's answer:
var imageView: UIImageView!
var cachedImageViewSize: CGRect!
override func viewDidLoad(){
super.viewDidLoad()
self.imageView = UIImageView(image: UIImage(named: "church-welcome.png"))
self.imageView.contentMode = .ScaleAspectFill
self.cachedImageViewSize = self.imageView.frame
self.tableView.addSubview(self.imageView)
self.tableView.sendSubviewToBack(self.imageView)
self.tableView.tableHeaderView = UIView(frame: CGRectMake(0, 0, self.view.frame.size.width, 170))
}
func scrollViewDidScroll(scrollView: UIScrollView) {
var y: CGFloat = -scrollView.contentOffset.y
if y > 0 {
self.imageView.frame = CGRectMake(0, scrollView.contentOffset.y, self.cachedImageViewSize.size.width + y, self.cachedImageViewSize.size.height + y)
self.imageView.center = CGPointMake(self.view.center.x, self.imageView.center.y)
}
}
The above mentioned solutions will actually not really provide the exact implementation of how it is in Tweetbot or Tinder [target's profile picture zooming].
I also had to solve this problem and the most perfect implementation I found is to use 2 main elements:
1) Tableview which has content inset
2) Imageview on top of that table view which has an outlet for top constraint and height constraint and which is on top of that empty space provided by the tableview content inset.
So first make sure to set the insets for the tableView and initial height constraint for the imageView. You can do this in viewDidLoad for example.
imageViewHeightConstraint.constant = view.frame.height * 0.3
tableView.contentInset = UIEdgeInsets(top: view.frame.height * 0.3, left: 0, bottom: 0, right: 0)
Then just listen to thew scrollViewDidScroll delegate method and modify the constraints accordingly.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let contentOffset = scrollView.contentOffset.y
if -contentOffset >= view.frame.height * 0.3 {
//reset the top constraint
topConstraint.constant = 0
//make the imageview bigger by the additional content offset value
imageViewHeightConstraint.constant = -contentOffset
} else {
topConstraint.constant = -(view.frame.height * 0.3 + contentOffset)
}
}
You may need to inverse the signs according to how you've set your own constraints. And probably there's a way to optimize and not modify the constraints that often, but you get the idea.
Please also note that you need to:
imageView.contentMode = .scaleAspectFill
layer.masksToBounds = true
Otherwise the image will be drawn on top of your first tableView cells [dependent on the image size it may come to cover to whole table view] because it is drawn outside its "bounds".