I am having trouble getting a label in a UICollectionViewCell to dynamically resize based on length of string. I have subclassed the cell and linked the label to it. I have set the number of lines to 0 and still not dice.
I also call the sizeToFit() method.
Compiling for iOS 8.2
Here is my custom Cell code:
class ItemCollectionCell: UICollectionViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var reviewerLabel: UILabel!
#IBOutlet weak var statusImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
self.selected = false
titleLabel.numberOfLines = 0
titleLabel.sizeToFit()
}
}
Make sure that you didn't uncheck Autoresize subviews in InterfaceBuilder:
You have to tell the collection view what size the cell should be. Use this method of the UICollectionViewDelegateFlowLayout protocol:
collectionView:layout:sizeForItemAtIndexPath:
You can calculate the size by calling NSString UIKit Additions's method
boundingRectWithSize:options:attributes:context:
and passing the font information in the attributes argument.
Ok... So thank you to those who have helped. I have found out that my issue was indeed related to Auto-Layout constraints. I simply missed the box that said "Constrain to Margin".
Related
I'm kinda new to Swift coding and I wanted to train myself by finding some layouts and try to implement them using AutoLayout.
Here is a screen shot of a screen I try to implement
https://i.imgur.com/9yzvnzp.png
The behaviour is wanted here is that on scrolling the picture should fade away and the title should be set like so
https://i.imgur.com/bLhyTGs.png
Could anybody help me on how am I supposed to do that ?
Should I use a ScrollView ? UITableView ? UICollectionView ?
I'm actually working on an application that does something similar.
So, the first thing you want to do is create a UIView for the background, i.e. the blue part that shows Squirtle. This view can take up the entire view controller. Connect it to your UIViewController via IBOutlet and call it backgroundView.
Next, we want to lay a UIScrollView over backgroundView. This scroll view should be constrained to the top, bottom, leading, and trailing edges of the superview so that it covers the entire view controller's frame. Connect this UIScrollView to your UIViewController via IBOutlet and name it scrollView. Also, make sure that scrollView's backgroundColor is set to clear. This way, we'll be able to see backgroundView underneath our scrollView.
Within your UIViewController, you'll want to set scrollView's delegate to self inside of viewDidLoad. Your UIViewController's code should now look something like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
#IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
We'll set delegate methods for scrollView later. These will help us know when the scroll view has begun scrolling to the top of our SquirtleViewController.
Now, our scrollView has a clear background, and doesn't actually display anything to our user. This is how we want it for now. We want the top of our scrollView to be clear so that we can see backgroundView behind it, and the bottom of our scrollView to have another view, i.e. the "content view" that shows STATS, EVOLUTIONS, MOVES, HP, etc.
So, let's add another UIView as a subview of our scrollView. Connect it to SquirtleViewController via IBOutlet and name it contentView.
Now, we need to create a constraint between contentView's top edge and scrollView's top edge. This constraint's constant needs to be equal to the height of backgroundView's content. This way, our contentView won't cover up what we want to see from backgroundView. We should also save this height as backgroundViewContentHeight so that we can reference it later.
We also need contentView's leading, trailing, and bottom constraints to be equal to those of its superview, i.e. scrollView. These do not need to be connected via IBOutlets.
Also, give contentView a height and width constraint and connect these as contentViewHeight and contentViewWidth respectively to SquirtleViewController via IBOutlet. This will help us set the contentSize of our scrollView later.
Your code should now look something like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//New lines of code
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
Now, when SquirtleViewController lays out its subviews, we are going to want to set the constant properties of contentViewHeight and contentViewWidth. This will set a contentSize for our scrollView. We want our contentViewWidth to simply be the size of our SquirtleViewController's view's width. Our contentViewHeight's constant will be a little different though, as it depends on the height of our contentView's subviews. For this example, we'll say the contentViewHeight's constant should be 1200. The code should now look like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
}
//New lines of code
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewWidth.constant = view.frame.width
contentViewHeight.constant = 1200
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
From here, you can add subviews to backgroundView and contentView as you see fit in order to show the pokemon (backgroundView) as well as the pokemon's stats (contentView). But, you still need to know how to change the content of your backgroundView depending upon how much we have scrolled the scrollView. This is where the UIScrollViewDelegate helps us out.
There is a method in UIScrollViewDelegate that is called every time our scrollView's contentOffset.y changes. This basically means every time we change the amount that our scrollView has scrolled, this method will be called.
Inside of this method, we can cross reference the amount we've scrolled with the height of the background view. As our scrollView's contentOffset.y approaches the height of our backgroundView's content, we can fade out the image of Squirtle and fade in a UILabel that simply says "Squirtle" (like in your example).
So, in your case, I would suggest adding a UIImage of Squirtle as a subview of contentView and connect it via IBOutlet as pokemonImageView. pokemonImageView should have its frame partially outside of the contentView (like in your example). If you do this, ensure that contentView's clipsToBounds property is set to false.
I would also add a UILabel as a subview of backgroundView and connect it to SquirtleViewController via IBOutlet as pokemonNameLabel. When our SquirtleViewController's view loads, we should set pokemonNameLabel.alpha equal to zero so that it is initially hidden.
Your code should now look like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
//New line of code
#IBOutlet var pokemonNameLabel: UILabel!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//New line of code
#IBOutlet var pokemonImageView: UIImageView!
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
//New line of code
pokemonNameLabel.alpha = 0
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewWidth.constant = view.frame.width
contentViewHeight.constant = 1200
}
}
extension SquirtleViewController: UIScrollViewDelegate {
}
Now we simply need to add the scrollViewDidScroll method inside of our class extension. This is the method that will tell us our scrollView's contentOffset.y property so that we know how much we've scrolled. As this number increases, our pokemonImageView's alpha should decrease to zero and our pokemonNameLabel's alpha should increase towards one.
Inside of this method, I would divide our scrollView.contentOffset.y by the height of our backgroundView minus the height of the pokemonNameLabel. We can then use this decimal to set our respective alphas of pokemonImageView and pokemonNameLabel.
Your code should now look something like this:
class SquirtleViewController: UIViewController {
#IBOutlet var backgroundView: UIView!
//New line of code
#IBOutlet var pokemonNameLabel: UILabel!
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var contentView: UIView!
//New line of code
#IBOutlet var pokemonImageView: UIImageView!
//Our constraints
#IBOutlet var contentViewTop: NSLayoutConstraint!
#IBOutlet var contentViewHeight: NSLayoutConstraint!
#IBOutlet var contentViewWidth: NSLayoutConstraint!
var backgroundViewContentHeight = 400
override func viewDidLoad() {
super.viewDidLoad()
scrollView.delegate = self
//New line of code
pokemonNameLabel.alpha = 0
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentViewWidth.constant = view.frame.width
contentViewHeight.constant = 1200
}
}
extension SquirtleViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
//Subtracting contentView.frame.height from scrollView.frame.height
let alphaDecimal = scrollView.contentOffset.y / (backgroundViewContentHeight - pokemonNameLabel.frame.height)
pokemonNameLabel.alpha = alphaDecimal
pokemonImageView.alpha = 1 - alphaDecimal
}
}
Now, as we scroll up and what can be seen of the backgroundView shrinks, our pokemonImageView will fade out and our pokemonNameLabel will fade in. There will be a point in-between where both will have an alpha of 0.5. You can mess with this scrollViewDidScroll method as you see fit to make this work best for you.
I want to create a tableview with foods. Every cell must have:
1. Image
2. Title
3. Description
I want to move the image on the left of the cell but I can't. As you can see there is a "border on the left of the cell".
I have write this code to remove the 15px left border that apple has as default:
tableView.separatorInset = .zero
tableView.layoutMargins = .zero
The image has left constraints 0 but nothing happens.
Also if I remove the image from storyBoard, nothing happens. It's still there. (If I delete cell.imageView?.image = item.image from my code the image will removed)
Also As you can see I have constraints between image and labels but the text is behind the image.
What can I do? Thanks!
I think it's because you're using the UITableViewCell default ImageView, you should name your imageView with a different name from 'imageView'
iDevid I followed the steps from link https://www.ralfebert.de/ios-examples/uikit/uitableviewcontroller/custom-cells/ that you send me. I solved my problem with the left border but now I have problem with the constraints.
I want a screen like this:
But the result is like this:
Here is my class for the custom cell:
class HeadlineTableViewCell: UITableViewCell {
#IBOutlet weak var headLineImageView: UIImageView!
#IBOutlet weak var headLineTitleLabel: UILabel!
#IBOutlet weak var headLineDescriptionLabel: UILabel!
#IBOutlet weak var headLineIngredientsLabel: UILabel!
#IBOutlet weak var headLinePriceLabel: UILabel!
}
I have give them text:
cell.headLineTitleLabel?.text = item.name
cell.headLineDescriptionLabel?.text = item.description
cell.headLineIngredientsLabel?.text = item.ingredients
cell.headLinePriceLabel?.text = ("\(item.price)0€")
And the constraints are:
For the imageView:
For the Label 1:
For the Label 2:
For the Label 3:
For the Label 4:
Can anybody help me?
demo image
When I update a number to label will make the view get back to original position.
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBOutlet var aview: UIView!
var number = 0
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func plus(sender: UIButton) {
number++
label.text = "number:\(number)"
}
#IBAction func move(sender: AnyObject) {
aview.frame.origin.y -= 20
}
}
I couldn't find the answer on web, please help me to fix this problem.Thank you very much!
Because your xib or Storyboard you set use Autolayout:
So if you don't set constrain system will auto generate it. When you change frame by set frame it effect but when you access to it. It will auto back to old position.
if you don't want it happen. You set in viewDidLoad:
self.view.translatesAutoresizingMaskIntoConstraints = false
Issue is probably related to a constraint reloading when the label reloads.
Instead of setting aview.frame.origin.y -= 20 you should make an outlet to the constraint holding the y position of your aView and then update the constant of that constraint outlet instead.
I am trying to add a custom view programmatically to my view controller. I used this snippet of code with no success of it appearing in front of my main view controller.
var DynamicView = CustomFilter()
DynamicView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(DynamicView)
CustomFilter Class:
import UIKit
class CustomFilter:
UIView {
#IBOutlet weak var party: UIButton!
#IBOutlet weak var outdoors: UIButton!
#IBOutlet weak var sports: UIButton!
#IBOutlet weak var diner: UIButton!
#IBOutlet weak var music: UIButton!
#IBOutlet weak var gaming: UIButton!
}
The custom filter is connected to a xib file.
Xib File:
Is there a possibility that the custom view maybe out of placed? I'm not sure what I'm doing wrong.
In order to use a view we designed in a xib, we most load from the xib.
if
let bundle = NSBundle(forClass: CustomFilter.self),
let nib = bundle.loadNibNamed("<#Xib File Name#>", owner: self, options: nil),
let dynamicView = nib.first as? CustomFilter {
self.view.addSubview(dynamicView)
}
An alternative approach would be to write your CustomFilter's init to load the view from the xib itself.
More clearly, the problem you're having is that none of your CustomFilter's initializers are going to care about the xib file you made unless you write them and tell them to care about it. Your current code is returning a 0x0 view with probably a white or clear background. If you modified your current code to set the CustomFilter's frame to something other than 0x0 size and set the background color to something like UIColor.greenColor(), you'd see it clear as day.
Also, you could use Xcode's visual debugger to find it.
It's probably zero height and zero width, and may be off-screen also.
You need to give it height and width constraints and x and y position constraints.
You should probably also use CustomFilter(frame: someFrameRect), since as I recall initWithFrame is the designated initializer for UIView.
As an aside, variable names should start with a lower-case letter, so DynamicView should be dynamicView
class ViewController: UIViewController {
#IBOutlet weak var grayView: UIView!
#IBOutlet weak var lightGrayView: UIView!
#IBOutlet weak var smileyFaceIMG: UIImageView!
#IBAction func smileyMove(sender: UIPanGestureRecognizer) {
var point = sender.locationInView(view)
smileyFaceIMG.center = point
if CGRectContainsPoint(lightGrayView.frame, smileyFaceIMG.center) {
smileyFaceIMG.image = UIImage(named: "Smiley_Face.jpg")
}
}
}
I have set up a UIImage which is supposed to change the image after I go over another UIView. This method seems to work with a regular UIView, however not with UIImage. How can I move the UIImage with the UIPanGestureRecognizer.
I learned what my mistake was. I needed to create a regular UIView, and then drag the UIImgView onto it. Then replace the code from the UIImageView to the UIView. Now everything works properly.