I have UICollectionView with a lot of cells. The cells have images on them that need to be blurred. That's why I am adding UIVisualEffectView with blur effect to the image views. The cells are like pages - every cell takes up the whole screen.
The problem is with the second cell. When it appears the blurred view is added visibly and it's ugly. The rest of the cells have the blurred view already when they become visible on the screen and are okay. This is the code I am using to add the blur view:
class ImageCell: UICollectionViewCell {
#IBOutlet weak var mainImageView: UIImageView!
#IBOutlet weak var backgroundImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
let regularBlur = UIBlurEffect(style: .regular)
let blurView = UIVisualEffectView(effect: regularBlur)
blurView.frame = backgroundImageView.bounds
blurView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
backgroundImageView.addSubview(blurView)
}
}
Modifying your code:
class ImageCell: UICollectionViewCell {
#IBOutlet weak var mainImageView: UIImageView!
#IBOutlet weak var backgroundImageView: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
backgroundImageView.isHidden = true
let regularBlur = UIBlurEffect(style: .regular)
let blurView = UIVisualEffectView(effect: regularBlur)
blurView.frame = backgroundImageView.bounds
blurView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
backgroundImageView.addSubview(blurView)
backgroundImageView.isHidden = false
}
}
You can code like this:
class ImageCell: UICollectionViewCell {
#IBOutlet weak var mainImageView: UIImageView!
#IBOutlet weak var backgroundImageView: UIImageView!
let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
override func awakeFromNib() {
super.awakeFromNib()
blurView.frame = backgroundImageView.bounds
blurView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
backgroundImageView.addSubview(blurView)
}
override func prepareForReuse() {
blurView.isHidden = true
}
}
Related
I have a bit of a complicated UI structure.
I have a collection view. For the collection view's cells, I have a custom xib. Inside that custom xib, I load another custom xib into a UIView.
Here is a picture of my collection view cell's custom xib:
https://i.stack.imgur.com/GQLpj.png
I have also highlighted the UIView (Video Container View) that I load another xib into. Here is what that xib looks like:
https://i.stack.imgur.com/jpSi0.png
With the collection view, I do some custom resizing of the cells by making the view 16:9 ratio:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// Get the width of the screen
let width = UIScreen.main.bounds.width
let imageWidth = width
let imageHeight = imageWidth / (16 / 9)
self.postMediaHeight = imageHeight
return CGSize(width: imageWidth, height: imageHeight)
}
My PostMediaCollectionViewCell class looks like this:
class PostMediaCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var container: UIView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var moreView: UIView!
#IBOutlet weak var moreViewLabel: UILabel!
#IBOutlet weak var videoContainerView: VideoContainerView!
override func awakeFromNib() {
super.awakeFromNib()
}
func initialize() {
//
}
}
And my VideoContainerView class looks like this:
class VideoContainerView: UIView {
#IBOutlet var rootView: UIView!
#IBOutlet weak var videoView: VideoView!
#IBOutlet weak var playIconContainer: UIView!
#IBOutlet weak var timeContainer: UIView!
#IBOutlet weak var timeLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
func initialize() {
Bundle.main.loadNibNamed("VideoContainerView", owner: self, options: nil)
rootView.translatesAutoresizingMaskIntoConstraints = false
addSubview(rootView)
rootView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
rootView.topAnchor.constraint(equalTo: topAnchor).isActive = true
rootView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
rootView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
}
}
But when I run the simulator, the VideoContainerView doesn't seem to fit the superview's (the cell's) constraints/frame, as you can see in the screenshot below. First, there's a white space above the video, and second, the video's resolution is stretch past the container.
One interesting thing I noticed is that if I scroll down past the cell and then scroll back up, it shows the video perfectly.
What am I doing wrong, and what am I missing here?
Update
I just printed out the bounds of a number of views and noticed that the videoView bounds are different from its parents bounds:
rootview bounds: (0.0, 0.0, 661.0, 410.0)
videoview bounds: (0.0, 0.0, 654.0, 593.0)
What should I do here?
You are missing constraints while adding rootView as subview of VideoContainerView. In initialize add following constraints.
func initialize() {
Bundle.main.loadNibNamed("VideoContainerView", owner: self, options: nil)
rootView.translatesAutoresizingMaskIntoConstraints = false
addSubview(rootView)
rootView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
rootView.topAnchor.constraint(equalTo: topAnchor).isActive = true
rootView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
rootView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
}
container is the main view of VideoContainerView
Change initialize method code as below and try to run your code
func initialize() {
let view = Bundle.main.loadNibNamed("VideoContainerView", owner: self, options: nil)?.first as! UIView
addSubview(view)
container.frame = self.bounds
container.autoresizingMask = [.flexibleWidth,.flexibleHeight]
}
I have used the code as below. But it only blur one position. Now I want to blur multiple positions at once
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btn_blur(_ sender: Any) {
// 1
let darkBlur = UIBlurEffect(style: .dark)
// 2
let blurView = UIVisualEffectView(effect: darkBlur)
blurView.frame = CGRect(x: 20, y: 30, width: 50, height: 50)
// 3
imageView.addSubview(blurView)
}
}
You could store an array of frames in your view controller, and fill that with your server response. Then use those frames to create the blur views.
import UIKit
class ViewController: UIViewController {
var frames = [CGRect]() // load this from your server data
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btn_blur(_ sender: Any) {
let darkBlur = UIBlurEffect(style: .dark)
for frame in frames {
let blurView = UIVisualEffectView(effect: darkBlur)
blurView.frame = frame
imageView.addSubview(blurView)
}
}
}
I want to create a tab bar at the top of my page and contain only text, and when one clicked it will change the text color and underline the selected item... as shown in the image below...
I searched a lot but didn't find something to help me achieve this..
Can someone please tell me how can i do this?
Take Three Buttons and three imageviews.take action for three buttons and take outlets for images and labels as given below . use this link to easy identify.it's perfectly working for you. click here
class ViewController: UIViewController
{
#IBOutlet weak var imgDetails: UIImageView!
#IBOutlet weak var imgPassword: UIImageView!
#IBOutlet weak var imglocations: UIImageView!
#IBOutlet weak var lblDetailsTittle: UILabel!
#IBOutlet weak var lblPasswordTittle: UILabel!
#IBOutlet weak var lblLocationTittle: UILabel!
override func viewDidLoad()
{
super.viewDidLoad()
//if you want select by default Details use this code otherwise uncomment it.
imgDetails.backgroundColor = UIColor.green
imgPassword.backgroundColor = UIColor.clear
imglocations.backgroundColor = UIColor.clear
lblDetailsTittle.textColor = UIColor.black
lblPasswordTittle.textColor = UIColor.lightGray
lblLocationTittle.textColor = UIColor.lightGray
}
#IBAction func btnDetailsTouchUpInSide(_ sender: Any)
{
imgDetails.backgroundColor = UIColor.green
imgPassword.backgroundColor = UIColor.clear
imglocations.backgroundColor = UIColor.clear
lblDetailsTittle.textColor = UIColor.black
lblPasswordTittle.textColor = UIColor.lightGray
lblLocationTittle.textColor = UIColor.lightGray
}
#IBAction func btnPasswordTouchUpInSide(_ sender: Any)
{
imgPassword.backgroundColor = UIColor.green
imgDetails.backgroundColor = UIColor.clear
imglocations.backgroundColor = UIColor.clear
lblPasswordTittle.textColor = UIColor.black
lblLocationTittle.textColor = UIColor.lightGray
lblDetailsTittle.textColor = UIColor.lightGray
}
#IBAction func btnLocationsTouchUpInSide(_ sender: Any)
{
imglocations.backgroundColor = UIColor.green
imgDetails.backgroundColor = UIColor.clear
imgPassword.backgroundColor = UIColor.clear
lblLocationTittle.textColor = UIColor.black
lblPasswordTittle.textColor = UIColor.lightGray
lblDetailsTittle.textColor = UIColor.lightGray
}
}
I have a UICollectionViewCell with a gradient subview.
In my view controller, I have buttons that are sorting CoreData and then reloadData() on the UICollectionView.
To avoid my gradient subview to be drawn again and again (as it happened in the past), I implement removeFromSuperview() in prepareForReuse().
There I also implement a flag that keeps track of gradient existence so I still add a new gradient when cell is load.
However after removing the gradient, my willMove(toSuperview: ) doesn't work, and gradient view doesn't appear.
What's wrong with my logic?
class CollectionCell: UICollectionViewCell {
#IBOutlet weak var mealImg: UIImageView!
#IBOutlet weak var mealTitleLbl: UILabel!
#IBOutlet weak var gradientView: UIView!
var gradientWasRemoved = false
func configureCell(meal: Meal) {
mealTitleLbl.text = meal.title
let img = meal.getMealImage()
mealImg.image = img
addGradient()
}
func addGradient () {
let gradient = CAGradientLayer()
gradient.frame = gradientView.bounds
let topColor = UIColor(red:0.07, green:0.07, blue:0.07, alpha:1)
let botomColor = UIColor.clear
gradient.colors = [topColor.cgColor, botomColor.cgColor]
gradientView.layer.insertSublayer(gradient, at: 0)
if gradientWasRemoved == true {
gradientView.willMove(toSuperview: self)
}
}
override func prepareForReuse() {
super.prepareForReuse()
gradientView.removeFromSuperview()
gradientWasRemoved = true
}
}
I was able to fix the logic with following.
In UICollectionViewCell class:
import UIKit
class CollectionCell: UICollectionViewCell {
#IBOutlet weak var mealImg: UIImageView!
#IBOutlet weak var gradientView: UIView!
#IBOutlet weak var mealTitleLbl: UILabel!
var gradientWasRemoved = false
func configureCell(meal: Meal) {
mealTitleLbl.text = meal.title
let img = meal.getMealImage()
mealImg.image = img
addGradient()
}
func addGradient () {
let gradient = CAGradientLayer()
gradient.frame = gradientView.bounds
let topColor = UIColor(red:0.07, green:0.07, blue:0.07, alpha:1)
let botomColor = UIColor.clear
gradient.colors = [topColor.cgColor, botomColor.cgColor]
if gradientWasRemoved == false {
gradientView.layer.insertSublayer(gradient, at: 0)
} else if gradientWasRemoved == true {
self.addSubview(gradientView)
}
}
override func prepareForReuse() {
super.prepareForReuse()
gradientView.removeFromSuperview()
gradientWasRemoved = true
}
}
In prepareForReuse( ) I didn't delete gradientView, I removed it from the Superview. I set the flag there that it was removed.
Since I didn't nil my gradientView i was able to run addGradient() and access gradientView.bounds for creation of new CGGradientLayer.
Right before adding a new layer to gradientView, I performed if check. If we did remove gradientView then we don't add a new CGGradientLayer but simply put our gradientView back.
This way we add CGGradientLayer to our gradientView only once.
I learned that by removing views from Superview they are still alive and can be edited.
Thank you #Luis and #LeoDabus for your contribution.
I'm making an application where I need an x amount of custom UIView and place them in a scrollView. The layout of the scrollView and the UIView xib are set with AutoLayout. The result I'm getting now is this:
First View is well centred in ScrollView
Second View is wrong and has a lot of space in between
See my VC Code under here.
let sponsors = Sponsors.createSponsors() <-- Array
override func viewDidLoad() {
super.viewDidLoad()
configureSponsors()
}
//MARK: Load the AddView in ScrollView
func configureSponsors() {
self.scrollView.contentSize = CGSizeMake(CGFloat(sponsors.count) * self.scrollView.frame.size.width, self.scrollView.frame.size.height)
for sponsor in sponsors {
numberOfItems++
let addView = NSBundle.mainBundle().loadNibNamed("AddView", owner: self, options: nil).last as! AddView
addView.addDataToAddView(sponsor)
addView.frame = CGRectMake(CGFloat(numberOfItems - 1) * scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height)
self.scrollView.addSubview(addView)
}
}
And here is my UIView code:
//MARK: Outlets
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var sponsorTitle: UILabel!
#IBOutlet weak var sponsorLogo: UIImageView!
#IBOutlet weak var sponsorSubtitle: UILabel!
#IBOutlet weak var roundView: UIView!
#IBOutlet weak var playMusicButton: UIButton!
//MARK: Properties
var cornerRadius: CGFloat = 3.0
func addDataToAddView(sponsor: Sponsors) {
backgroundImageView.image = UIImage(named: sponsor.backgroundImage)
sponsorLogo.image = UIImage(named: sponsor.logoImage)
sponsorTitle.text = sponsor.title
sponsorSubtitle.text = sponsor.subTitle
}
override func layoutSubviews() {
roundView.layer.cornerRadius = roundView.frame.size.width / 2
roundView.alpha = 0.7
roundView.clipsToBounds = true
}
//MARK: PlayVideo
#IBAction func playVideo(sender: UIButton) {
//play music
}
I've already searched for the same problems. I found out that I have to use the Pure Auto Layout Approach. Should that mean I need to programatically set the constraints for the UIView?
Many thanks,
Dax
Update:
Pictures for scrollView setup:
You should not perform layout calculations in viewDidLoad as frames are not set correctly till then.
Correct place to do this is either viewWillLayoutSubviews or viewDidLayoutSubviews as you will get the correct frame data when these functions are called