How to make a stretchable header view collection view [Swift] - swift

In my swift app I've a collection view and I want to creare a stretchable header view like this in table view: https://medium.com/if-let-swift-programming/how-to-create-a-stretchable-tableviewheader-in-ios-ee9ed049aba3

You already answered your question yourself with that article link, unless I miss something.
I will copy & paste for you and others that may have the same question, if it helps, because it even ships with a github link (kudos to Abhimuralidharan # Medium):
Create a tableview with the basic datasource and delegate methods which are required to simply load the table with some data.
Set the tableview’s contentInset property:
tableView.contentInset = UIEdgeInsetsMake(300, 0, 0, 0)
Here, I set the top value as 300 which is a calculated number which I will set as the initial normal height for the header imageview. Now, that we set the contentInset , the tableview’s frame will start at (0,0) and the first cell will start at (0,300).
Now, create an imageview with height 300 and add it to the current View above the tableview.
imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 300)
imageView.image = UIImage.init(named: “poster”)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
view.addSubview(imageView)
Then, add the following code in the scrollview delegate method scrollViewDidScroll which gets called every time the tableview is scrolled.
func scrollViewDidScroll (_ scrollView: UIScrollView) {
let y = 300 — (scrollView.contentOffset.y + 300)
let height = min(max(y, 60), 400)
imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: height)
}
Compile and run the code. Full source code is available in github.

Related

How can i get the absolute position of an UISlider inside of a View

i have created a subview with a lot sliders in it
var sliderArea = UIView()
sliderArea = UIView(frame: CGRect(x: 300, y: 400, width: 500, height: 100)
view.addSubview(sliderArea)
mySlider1 = setUpSlider(sliderNr: 1, ypos: 30)
mySlider2 = setUpSlider(sliderNr: 2, ypos: 30)
mySlider3 = setUpSlider(sliderNr: 3, ypos: 30)
sliderArea.addSubview(mySlider1)
sliderArea.addSubview(mySlider2)
sliderArea.addSubview(mySlider3)
i have a lot of subviews similar to the "sliderArea" to be able to change my sliders quickly while the layout adopts automatically
now i need to know where the absolute position of each slider is to place buttons on top of it. i need to have all this buttons inside an extra view on top of it all. any ideas? thank you
You should convert your slider frame to window's coordinate system using this method of CGRect:
https://developer.apple.com/documentation/uikit/uiview/1622504-convert
Just pass nil as second parameter and it will return you an absolute frame.

Button with multiple accessible labels and images in Swift

I need to create custom button class, reuse it 4 times and I also need to override its text and image name. My next problem is how to set its frame somehow dynamically (now it is static), because I need this 4 buttons in grid 2x2.
I'm trying to create button exactly like this: https://imgur.com/a/dNhUGhc.
I have coded this but it is static and in ViewController I can't edit (override) these labels and image name. And if I tried to reuse this class I would have them in the same spot, because frame settings is exactly the same.
I'm subclassing UIButton. If something more suitable exists just let me know.
Code for adding label
// city label
let cityRect = CGRect(x: 0, y: 20, width: buttonWidth, height: 25)
let cityLabel = UILabel(frame: cityRect)
cityLabel.text = "Label"
cityLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
cityLabel.textAlignment = .center
addSubview(cityLabel)
Code for adding image
// image
let imageView = UIImageView(image: UIImage(named: "something"))
imageView.frame = CGRect(x: 0, y: 60, width: 40, height: 40)
imageView.center.x = self.center.x - 20
addSubview(imageView)
Can you guys help me? Thanks
It looks like what you need to do is use an IBOutlet. Basically, an IBOutlet will give you a reference within your code (custom UIView or UIViewController subclass) to the button that you've setup in xib or storyboard. Then you can make any changes or adjustments that you want to it at runtime.
Check this out to learn more about IBOutlets and how to set them up in your project.

UIImageView: applying multiple UIViewContentMode properties to one UIImageView?

How can I add UIViewContentMode.center to this UIImageView while also keeping .scaleAspectFill?
func insertImage() {
let theImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 300))
theImageView.image = #imageLiteral(resourceName: "coolimage")
theImageView.contentMode = UIViewContentMode.scaleAspectFill
view.addSubview(theImageView)
}
Furthermore, can somebody explain to me what the "view" exactly is in the last line "view.addSubview(theImageView)"? Is it the mysterious "view hierarchy" that I read about? Why can't I simply initialize the UIImageView? Why must it be bound to something called "view" that I haven't explicitly created? There is only a UIViewController and a UIImageView so far.
As far as I know, you can't set content mode to both aspect fit and center. However, center will do what aspect fit does providing the image size is smaller than the size of the imageView. If not, use aspect fit. The following code ought to allow you to differentiate between the two:
if (theImageView.bounds.size.width > UIImage(named: "coolimage")?.size.width && theImageView.bounds.size.height > UIImage(named: "coolimage")?.size.height) {
theImageView.contentMode = .aspectFit
} else {
theImageView.contentMode = .center
}
As for the second part of your question, I'll refer you to this thread, which has a fairly comprehensive explanation of UIViewController vs UIView. Hope that helps.

Setting multiple values dynamically

I have a horizontal scroll view which contains 20 images. At the moment I have set all the images one by one and created an image view 20 times.
Something like this 20 times:
let imageView1 = UIImageView(frame: CGRect(x: calculateWidth(), y: 0, width: 60, height: 60))
imageView1.image = photos[0]
self.imageScrollView.addSubView(imageView1)
This look horrible. Can I optimize this in any way?
You seem to have an array or photos, why not just iterate through that array and create the imageViews. Something like this:
for photo in photos {
let imageView = UIImageView(frame: CGRect(x: calculateWidth(), y: 0, width: 60, height: 60))
imageView.image = photo
self.imageScrollView.addSubView(imageView)
}
You can iterate through your photos using a for loop, adding the images accordingly and changing the frame with every iteration:
var frame = CGRect(x: calculateWidth(), y: 0, width: 60, height: 60)
for photo in photos {
let imageView = UIImageView(frame: frame)
imageView.image = photo
self.imageScrollView.addSubView(imageView)
//update frame here for the next iteration
frame.origin.y += 60
}
If your coordinates follow no specific pattern, you can put them in an array and set them accordingly.
Nonetheless, depending on what you are trying to accomplish with the images, if you are trying to list these images or have them in a table or grid-like form, I suggest using a UITableView or a UICollectionView as these reuse cells and have much better performance than just adding many images on screen.

How to update UILabel that is a subview of inputView?

I have UITextView where user types a text. When the keyboard is shown, I add inputView with UIlabel on it. I want this UIlabel to hold character length of the text. It seems very easy task, but unfortunatelly it does not update this word counter UILabel when user change text..
this is how I load the inputView
_textView.inputView = [self inputAccessoryView];
in inputAccessoryView I simply add UILabel as a subview. When keyboard is show, UILabel is also show with inputView. I track changes on
- (void)textViewDidChange:(UITextView *)textView
unfortunatelly the UILabel is never updated (redrawn). When I log in to console its value, the value is correct, so its updating, but the UIlabel is never redrawn and holds the default value.
Can anyone help me with this?
did you
_textView.delegate = self;
?
I know it was 5 years ago, but it might help others, who like me stumble upon your question.
I use a UIToolbar as my inputAccessoryView (in my case it has a label, a flexible separator and a button).
On textFieldEditingChanged event I rebuild part of the toolbar like this
#IBAction func textFieldEditingChanged(_ sender: UITextField) {
//get a reference to the toolbar
let toolBar = sender.inputAccessoryView as! UIToolbar
//create a new label and set size + other properties
toolbarLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 40, height: 22))
toolbarLabel.text = mySpecificCalculatedString(sender.text!)
toolbarLabel.font = defaultFont(17)
toolbarLabel.adjustsFontSizeToFitWidth = true
let width = toolbarLabel.textRect(forBounds: CGRect(x: 0, y: 0, width: maxWidthForLabel, height: 0), limitedToNumberOfLines: 1).size.width
toolbarLabel.frame = CGRect(x: 0, y: 0, width: width, height: 22)
toolbarLabel.textColor = .black
toolbarLabel.tag = 666
//rebuild the specific tolbar item
let customView = UIBarButtonItem(customView: toolbarLabel)
toolBar.items![0] = customView
}
Note: simply changing the text of the label did not work for me either, I had to reinitialize it.
Hope it helps.