Assigning "aspect fill" option for background image of uibutton Swift - swift

We can set the image of the button to be of aspect fill by :
imageView?.contentMode = UIViewContentMode.ScaleAspectFill
but since I am dealing with the background image, I'm not sure how to access contentMode for the background image.
I know that instead of doing so, I can create a custom class where the image can be centered with the button text centered on top, then set the imageview as aspect fill with the above code, but that seems like a lot more work to achieve the same result.
What's the best way to accomplish this?

It's been a long time since question asked but anyone who wonder,If you set your button image using setImage() it will show the image with its width and height no matter you scale, but If you set using setBackgroundImage() It will be just fine,didn't even need to scale

For whoever stumbles across this:
The UIImageView that contains backgroundImage is not publicly accessible. See https://developer.apple.com/documentation/uikit/uibutton, there is no such property. I do not why Apple protected that.
First solution
However you can filter your UIButton for all the UIImageViews and set their property to .scaleAspectFit:
override func layoutSubviews() {
super.layoutSubviews()
button
.subviews
.filter { $0 is UIImageView }
.forEach { $0.contentMode = .scaleAspectFit }
}
Second solution
The first solution could possibly conflict with any other UIImageViews you have in your UIButton. So you could also filter only for the exact background image:
override func layoutSubviews() {
super.layoutSubviews()
let backgroundImage = UIImage(named: "backspace-button")
let backgroundImageView = button
.subviews
.filter { $0 is UIImageView }
.first(where: { ($0 as? UIImageView)?.image == backgroundImage}) as? UIImageView
backgroundImageView?.contentMode = .scaleAspectFit
}
I would not recommend to access the backgroundImageView by its array index. There is no guarantee that this order will be like that forever.

Related

Custom TabBarController with active circle

After reading a few articles about custom UITabBarControllers, I am left more confused than before I even started doing the research in the first place.
My goal is to create a custom TabBar with 3 important properties:
No text, just icons
The active icon is marked by a circle filled with a color behind it, and therefore needs a different icon color
Here's what I am trying to achieve:
I've been able to remove the text and center the icon by following another StackOverflow answer (Remove tab bar item text, show only image), although the solution seems like a hack to me.
How would I go about creating a circle behind the item and change the active item's color?
Also, would someone mind explaining the difference between the XCode inspector sections "Tab Bar Item" and "Bar Item", which appear directly under each other?
The first step is simple: leaving the title property of the UITabbarItem empty should hide the label.
Your second step can actually be broken down into two steps: changing the color of the icon and adding a circle behind it.
The first step here is simple again: you can set a different icon to use for the currently selected ViewController (I use Storyboards, this process is pretty straightforward). What you'd do is add a white version of the icon to be shown when that menu option is selected.
The final step is displaying the circle. To do this, we'll need the following information:
Which item is currently selected?
What is the position of the icon on the screen?
The first of these two is pretty easy to find out, but the second poses a problem: the icons in a UITabBar aren't spaced around the screen equally, so we can't just divide the width of the tabbar by the amount of items in it, and then take half of that to find the center of the icons. Instead, we will subclass UITabBarController.
Note: the tabBar property of a UITabBarController does have a .selectionIndicatorImage property. You can assign an image to this and it will be shown behind your icon. However, you can't easily control the placement of this image, and that is why we still resort to subclassing UITabBarController.
class CircledTabBarController: UITabBarController {
var circle: UIView?
override func viewDidLoad() {
super.viewDidLoad()
let numberOfItems = CGFloat(tabBar.items!.count)
let tabBarItemSize = CGSize(width: (tabBar.frame.width / numberOfItems) - 20, height: tabBar.frame.height)
circle = UIView(frame: CGRect(x: 0, y: 0, width: tabBarItemSize.height, height: tabBarItemSize.height))
circle?.backgroundColor = .darkGray
circle?.layer.cornerRadius = circle!.frame.width/2
circle?.alpha = 0
tabBar.addSubview(circle!)
tabBar.sendSubview(toBack: circle!)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let index = -(tabBar.items?.index(of: tabBar.selectedItem!)?.distance(to: 0))!
let frame = frameForTabAtIndex(index: index)
circle?.center.x = frame.origin.x + frame.width/2
circle?.alpha = 1
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let index = -(tabBar.items?.index(of: item)?.distance(to: 0))!
let frame = frameForTabAtIndex(index: index)
self.circle?.center.x = frame.origin.x + frame.width/2
}
func frameForTabAtIndex(index: Int) -> CGRect {
var frames = tabBar.subviews.compactMap { (view:UIView) -> CGRect? in
if let view = view as? UIControl {
for item in view.subviews {
if let image = item as? UIImageView {
return image.superview!.convert(image.frame, to: tabBar)
}
}
return view.frame
}
return nil
}
frames.sort { $0.origin.x < $1.origin.x }
if frames.count > index {
return frames[index]
}
return frames.last ?? CGRect.zero
}
}
Now use this subclass of UITabBarController instead of the base class.
So why this approach over simply changing the icon to a circled one? Because you can do many different things with this. I wrote an article about animating the UITabBarController in a similar manner, and if you like, you can easily use above implementation to add animation to yours too.
The easiest and actually cleanest way to do it is to design your icons and import them as images to the .xcassets folder. Then you can just set the different icons for the different states for each of the viewControllers with:
ViewController.tabBarItem = UITabBarItem(title: "", image: yourImage.withRenderingMode(.alwaysOriginal), selectedImage: yourImage)
your selected image will be the one with the circle and the image will be without. It is way easier than manipulating the images in xcode and it is also less expensive since the compiler only has to render the images and doesn't have to manipulate them.
About the other question UIBarItem is
An abstract superclass for items that can be added to a bar that appears at the bottom of the screen.
UITabBarItem is a subclass of UIBarItem to provide extra funtionality.

Prevent image overlay in image view swift

I have a tableViewCell with image view.
Even though I clear the imageView and set the image I get the below unintended behaviour, where one image is overlaid over the other.
Please advice how this could be resolved.
Code on Resetting Image in ImageView:
self.profileImageLabelTVCell.cellImageView.image = nil // Clearing previous image
self.profileImageLabelTVCell.cellImageView.image = image
Here is the link for the tableViewCell code and roundedImageView class, that I've used. (Since SO did not allow the entire code to be posted here)
https://gist.github.com/pravishanth/14cdb8bf14dd8899b081bdc97988b985
Add your reset image code to the TableViewCell's
func prepareForReuse() method.
override func prepareForReuse() {
super.prepareForReuse()
cellImageView.image = nil
}

UITextView background image is scrolling for multiple lines

I have created extension for UITextView, where I set image to the textview using below code.
let backgroundImage = UIImageView(frame: ....)
backgroundImage.image = UIImage(named: "textarea.png")
self.addSubview(backgroundImage)
self.sendSubview(toBack: backgroundImage)
At start it looks fine as showing below image (left side), however as I add multi-line text, image also starts scrolling (right side), which is incorrect.
Any idea how to make image fixed as it is?
Below is what I did
let backgroundImage = UIImageView(frame: ....)
backgroundImage.image = UIImage(named: "textarea.png")
mainView.insertSubview(backgroundImage, belowSubview: self)
mainView is nothing but the main view (self.view) of view controller.
This is like we are not going to add backgroundImage in the subview of textview, instead we will be adding in the mainview (self.view).
A simple solution:
A container UIView contains background UIImageView and TextView(with clear background color).

NSSearchField: How to hide icon and border?

This is kind of a duplicate of this question. Because everything I know about Swift is Swift3, I`m wondering if someone could "translate" the suggested solution in this answer.
Also:
I made a NSSearchfield without border, put it in a framed view, and it still shows the gray border. I would be curious of how to disable the animated gray border and maybe even how to change the color of the gray "search" line.
My ugly result now looks like this:
It would be a big help if someone could tell me how to manage this difficult NSSearchfield.
//UPDATE
According to firstinq´s answer, the icon now disappeared, which is great. But still, there is this disturbing animated gray border. Which I can´t understand: The NSSearchFielt is inside a NSView (blue border). So everything outside the NSView should be hidden, right?. So why am I still seeing the gray border? cell.isBordered = falsehas no effect.
Any advice how to handle that?
This is how I draw the border of the NSView:
class SearchFieldBorder: NSView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
self.layer?.borderWidth = 1
self.layer?.borderColor = NSColor.blue.cgColor
}
}
To hide the icon: cast the cell to NSSearchFieldCell and set the cell's searchButtonCell to transparent. Possible swift3 version:
if let cell = self.searchField.cell as? NSSearchFieldCell {
cell.searchButtonCell?.isTransparent = true
}
Here searchField is an NSSearchField
To remove the focus border:
searchField.focusRingType = .none
To change grey line/cursor it would be better to subclass the NSSearchField and override the methods.
You can get an idea from here.
I'll supplement the answer above.
To hide the search icon, assign a nil to the SearchButtonCell property
if let cell = searchField.cell as? NSSearchFieldCell {
cell.searchButtonCell = nil
}

Swift - How to add a layer on a UIImageView inside of a UICollectionViewCell and keep the parallax effect

I'm working on a tvOS project. This project contains a UICollectionView with a UIImageView inside of each UICollectionViewCell. First of all I'm using adjustsImageWhenAncestorFocused on the image, because it looks good when the cell is in focus and I want to have the 'parallax' effect.
When a cell is in focus there should pop up an label over the image(for the title of a product) which is not a problem to make. But I want the title to be good readable so thats why I want to put on a gradient layer on top of the image.
The problem:
When I add the gradient layer to the UIImageView, it goes on top of it but it doesn't 'stick' to it. It should go with the image when the user is doing the parallax thing, but it stays at its position and the original image does move underneath the gradient layer
Screenshot of the problem
The code
let gradientLayer = CAGradientLayer()
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
gradientLayer.colors = [UIColor(red:0.00, green:1.00, blue:1.00, alpha:0.3).cgColor, UIColor(red:1.00, green:0, blue:1.00, alpha:0.3).cgColor]
gradientLayer.frame = productImageView.focusedFrameGuide.layoutFrame
gradientLayer.isHidden = true
self.productImageView.layer.addSublayer(self.gradientLayer)
if (self.isFocused){
gradientLayer.isHidden = false
}
else {
gradientLayer.isHidden = true
}
}
Additional question
Is this a bug? Because I still don't figured it out how to fix this problem. Should I give this to Apple?
Apple introduced overlayContentView in tvOS 11. You should add your label to this view and it will do the focus for you!
For more information read https://developer.apple.com/documentation/uikit/uiimageview/2882128-overlaycontentview?changes=latest_minor