UIButton's backgroundImage's content mode not respected - ios

I'm putting the finishing touches on an app I'm about to publish and I've enountered difficulty getting the contentMode of a UIButton's background image to be respected. Regardless of the contentMode I specify, the image just gets plopped in the background with no regard for the contentMode setting.
I've looked at a post covering this topic, but it doesn't appear to do anything in my circumstance.
This is called in from viewDidLoad:
// I tried putting the contentMode lines here...
myButton.imageView!.contentMode = .scaleAspectFit
myButton.contentMode = .scaleAspectFit
myButton.setBackgroundImage(UIImage(named: "myImage.png"), for: .normal)
// ...and here
// myButton.imageView!.contentMode = .scaleAspectFit
// myButton.contentMode = .scaleAspectFit
// I threw this in for S's & G's to see if it would work...it didn't
myButton.translatesAutoresizingMaskIntoConstraints = false
myButton.imageView?.translatesAutoresizingMaskIntoConstraints = true
I'm not sure what I'm missing, but I rest assured it will be something startling stupid on my part. I welcome suggestions on how I can get the contentMode of a UIButton's background image to be respected. Thank you for reading.
Update
The button is typed as a custom button, not a system button.

The problem is that you add an image using setBackgroundImage() method. This adds your image to some private UIImageView that you can't reach directly like myButton.imageView!. In your code, you're applying contentType for you button instead of its background image.
If you want to reach this background UIImageView you need to use subviews array. But if you try to do this inside your viewDidLoad() method, you'll find that background UIImageView hasn't been added yet because your buttons layout method wasn't called. There are 2 ways to fix that.
Solution 1 – call layoutIfNeeded explicitly:
override func viewDidLoad() {
super.viewDidLoad()
myButton.setBackgroundImage(UIImage(named: "myImage"), for: .normal)
myButton.layoutIfNeeded()
myButton.subviews.first?.contentMode = .scaleAspectFit
}
Solution 2 – move contentMode setup at viewDidAppear method.
override func viewDidLoad() {
super.viewDidLoad()
myButton.setBackgroundImage(UIImage(named: "myImage"), for: .normal)
myButton.setTitle("Some title", for: .normal)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myButton.subviews.first?.contentMode = .scaleAspectFit
}
Hope it helps you.

To simplify the accepted answer, all you need to do is layout the subviews and your setup code can be done in one place.
override func viewDidLoad() {
super.viewDidLoad()
myButton.setBackgroundImage(UIImage(named: "myImage.png"), for: .normal)
myButton.layoutIfNeeded()
myButton.subviews.first?.contentMode = .scaleAspectFill
}

A button's imageView property and backgroundImage property are two separate things.
Unlike the accepted answer, you can use the imageView property instead.
override func viewDidLoad() {
super.viewDidLoad()
myButton.setImage(UIImage(named: "myImage.png"), for: .normal)
myButton.imageView?.contentMode = .scaleAspectFill
}

Related

Background Image causing animated popViewController delay/lag

I am getting a delay on animation when popping a view controller.
The reason I am getting the delay is because I have a background image.
override func viewDidLoad() {
super.viewDidLoad()
setBackgroundImage()
}
#IBAction func navigationButton(_ sender: Any) {
print("navigation button presssed")
self.navigationController?.popViewController(animated: true)
}
func setBackgroundImage() {
let backgroundImage = UIImageView(frame: UIScreen.main.bounds)
backgroundImage.image = UIImage(named: "RewardsBackgroundONLY")
backgroundImage.contentMode = UIView.ContentMode.scaleAspectFill
self.view.insertSubview(backgroundImage, at: 0)
}
How do I prevent the delay from happening while setting the background image.
I also tried popping the view controller on the main thread, still doesnt work.
Probably your image is too large and the content mode backgroundImage.contentMode = UIView.ContentMode.scaleAspectFill cause this behavior.
you have two ways to resolve this problem.
change the UIView.ContentMode value and check if is ok for you. try scaleToFill or scaleAspectFit
resize image to fit on your app.
just set
clipsToBounds = true
it works for me

How to add an event to UIImageView subclass the way it would be in IB

I'm tired that UIButton's image is too complicated to customize. And it's long to create tappable UIImageView. Than i want to create own mixed element of UIImage and UIButton. It would let to create custom animation, touch areas and lot more.
I decided to take a UIImageView and add a gesture or a button to it to detect touches. But i need to have a TouchUpInside or just touch event to easily connect it in IB. Look at the code, what is already written:
class ClickableImageView: UIImageView {
let embeddedButton = UIButton()
override func awakeFromNib() {
super.awakeFromNib()
setup()
}
fileprivate func setup() {
isUserInteractionEnabled = true
sv(embeddedButton)
embeddedButton.fillContainer()
embeddedButton.addTarget(self, action: #selector(didPress), for: .touchUpInside)
}
#objc fileprivate func didPress() {
embeddedButton.preventRepeatedPresses()
}
}
I did the same with the UIButton subclassing and it works, but it not showing the image in IB, so it confuse the developer.
The last problem to solve is how to add the event?
UIButton image can be easily resized by giving distance from (top,left,bottom and right) of the UIButton like below.
yourButton.imageEdgeInsets = UIEdgeInsetsMake(10, 50, 10, 95)
If you are looking for adding an event to the UIImageView, do the following,
yourImage.isUserInteractionEnabled = true
let tapImage = UITapGestureRecognizer(target: self, action: #selector(myFunction(tapGestureRecognizer:)))
yourImage.addGestureRecognizer(tapImage)
Then handle your method,
#objc func myFunction(tapGestureRecognizer: UITapGestureRecognizer) {
//do something here
}

Is there a way to change the background colour of a UINavigationBar in swift 4

As the title implies I'm wondering if there is a way to change the background colour of a UINavigationBar in Swift. I know this question has been asked once before as I found this question here but I couldn't get any of the code to work when I tried to bring it up to date. I have linked the custom class which is a subclass of UINavigationController. Currently, the background colour of all the child UINavigationItem's have the default background colour of a slightly tinted white. I want to be able to change their colour to fit the rest of the UI elements in my app as most of them are extremely dark so I'm trying to change that below there is a picture of what I'm trying to replicate.
Is there is a more recent way to do this? Maybe using User defined runtime attributes. I'm out of ideas.
I've tried
#objc func changebarColor(){
self.navigationController?.navigationBar.barTintColor = UIColor.red;
}
override func viewDidLoad() {
super.viewDidLoad()
changebarColor()
}
And
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.barStyle = UIBarStyle.blackTranslucent
self.navigationController?.navigationBar.barTintColor = UIColor.red;
}
And
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController!.navigationBar .setBackgroundImage(UIImage(), for: UIBarMetrics.default)
self.navigationController!.navigationBar.shadowImage = UIImage();
self.navigationController!.navigationBar.isTranslucent = true;
self.navigationController!.navigationBar.backgroundColor = UIColor.red;
}
As well as all of the other answers posted on the page. Slightly tweaking them to remove errors.
You are on the right track, but you are calling it in the wrong place. You should move it to viewWillAppear which is called every time you view is going to be displayed. viewDidLoad() is only called once.
This means that another view controller or even the OS can change the colour of your navigation bar after viewDidLoad()
override func viewWillAppear(_ animated:Bool)
{
super.viewWillAppear( animated)
self.navigationController?.navigationBar.barTintColor = UIColor.red
}
Also, you don't need semi colons in Swift.

How to resize a large image and make it fit into UIButton in swift?

I am trying to place a large image on a button after resizing to the exact size of button. I am making use of this code
var FB = UIButton(type:UIButtonType.Custom) as UIButton
var image = UIImage(named: "facebook-logo-png-2.png")
func buttonTouchDown(sender:UIButton!){
print("button Touch Down")
}
override func viewDidLoad() {
super.viewDidLoad()
FB.frame = CGRectMake(0,0 , 100, 100)
FB.backgroundColor = UIColor.clearColor()
FB.addTarget(self, action: #selector(buttonTouchDown(_:)), forControlEvents: .TouchDown)
FB.setImage(image, forState: .Normal)
FB.imageEdgeInsets = UIEdgeInsetsMake(25,25,25,25)
self.view.addSubview(FB)}
I am not getting the desired output. Currently, my output looks like this screen shot
This is my first iOS app, please help me if possible
Thanks in advance
Use this line
FB.contentMode = .ScaleAspectFit
Before
self.view.addSubview(FB)
What it does is scale the image to the fit the size while being true to the aspect ratio of the image being set.

Cannot update UIButton.image after view loads

I am attempting something relatively simple. I have a UIButton that loads with an image:
#IBOutlet var peer5Outlet: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
peer5Outlet.enabled = true
var img = UIImage(named: "camera")
peer5Outlet.setImage(UIImage(named: "camera"),forState: UIControlState.Normal) }
Once it is loaded, I simply want to update that image by calling a method:
func updateButtonImage() {
peer5Outlet.imageView!.image = UIImage(named: "connected")!
print("peer5Outlet.imageView!.image = \(peer5Outlet!.imageView!.image!)")
}
However, I am still unable to update the image; the original image never changes in the view.
Perhaps I am missing some sort of view reload method?
Update your updateButtonImage function to
func updateButtonImage() {
peer5Outlet.setImage(UIImage(named: "connected"), forState: .Normal)
print("peer5Outlet.imageView!.image = \(peer5Outlet!.imageView!.image!)")
}
Try
peer5Outlet.setBackgroundImage(UIImage(named: "connected"), forState: UIControlState.Normal)
Edit: In order to use the setImage method, you need to change the button's type to "Custom"...
You need to use the main thread whenever you're touching UIImage on UI
GCD - main vs background thread for updating a UIImageView
func updateButtonImage() {
dispatch_async(dispatch_get_main_queue()) {
// update image
}
}

Resources