First, this was done programmatically and not in Storyboards.
I am using a background image for my UIViewController. It looks fine in portrait mode but because my image is not wide enough for landscape mode, the image becomes doubled (the same image is shown twice, side by side) when the device is tilted to landscape mode. Is there a way to switch to a different background image if the device is rotated to landscape mode in order to avoid this issue?
Please note: I am trying to do this without having to create an entirely new UIViewController for landscape mode.
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor(patternImage: UIImage(named: "backgroundimage.png")!)
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.view.backgroundColor = UIColor.clear
view.addSubview(inputsContainerView)
view.addSubview(loginRegisterButton)
view.addSubview(profileImageView)
view.addSubview(loginRegisterSegmentedControl)
setupInputsContainerView()
setupLoginRegisterButton()
setupProfileImageView()
setupLoginRegisterSegmentedControl()
}
Use UIImageView to set a background image instead of using UIColor(patternImage:) method. See the below code snippet
var backgroundImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
backgroundImageView = UIImageView(frame: CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.width, height: self.view.frame.height))
backgroundImageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(backgroundImageView)
}
I have created a backgroundImageView and set the image to that view. In order to handle portrait and landscape mode, autoresizingMask parameter is also set
Related
After seeking a lot and trying many solutions, nope fixed my problem.
In my app I customized the UINavigationController in order to have blur effect:
import UIKit
class CustomNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
let visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
visualEffectView.frame = (self.navigationBar.bounds.insetBy(dx: 0, dy: -40).offsetBy(dx: 0, dy: -40))
self.navigationBar.isTranslucent = true
self.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationBar.addSubview(visualEffectView)
self.navigationBar.sendSubviewToBack(visualEffectView)
}
}
Then in Main.storyboard I selected the customized class for the navigation controller item.
The blur effect works properly, the status icons are correctly visible, but not the standard navigation bar items: left button, title and right button.
For a moment they appears but soon after the customized navigation bar covers them.
I'm using Xcode 12.4 and I'm running the app on iPhone Xr.
How can I show the navigation bar elements again?
Thanks a lot in advance.
Translucent navigation bars in iOS already blur the content behind the bar, so you shouldn't need to add a UIVisualEffectView nor set a backgroundImage.
If you modify your code to just:
override func viewDidLoad()
{
super.viewDidLoad()
self.navigationBar.isTranslucent = true
}
does this not achieve the visual effect you're looking for?
If not, please try the following adjustment to your methodology:
override func viewDidLoad()
{
super.viewDidLoad()
self.navigationBar.isTranslucent = true
// create a UIImageView
let backgroundImage: UIImageView = UIImageView()
backgroundImage.autoresizingMask = [.flexibleWidth, .flexibleHeight]
backgroundImage.image = UIImage()
// add a blur effect to the ImageView
let visualEffectView: UIVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
visualEffectView.frame = (self.navigationBar.bounds.insetBy(dx: 0, dy: -40).offsetBy(dx: 0, dy: -40))
backgroundImage.addSubview (visualEffectView)
// and set that as your backgroundImage on the navigationBar
self.navigationBar.setBackgroundImage(backgroundImage.image, for: .default)
}
this adds the blur effect to the backgroundImage. This seems to work for me, but the visual effect is no different than just using my first suggestion, likely because backgroundImage.image == nil.
This is certainly an improved approach in that it doesn't add unexpected subviews into the UINavigationBar view hierarchy, and I observed both methods did not affect the visibility of the bar controls.
I have this view hierarchy on a view embedded in a UINavigationController:
When I scroll the UITableView the navigation bar is not moving up (the title is not becoming smaller) it stays like this:
If I remove the image view as background view everything works well.
My navigation is configured like this:
navigationItem.title = "Title here"
navigationItem.largeTitleDisplayMode = .always
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.barStyle = UIBarStyle.blackTranslucent
navigationController?.navigationBar.backgroundColor = .clear
A project demonstrating the problem is available here:
https://drive.google.com/file/d/181Aggala2ZfGN0lDjEtHWg0vobkM0iJc/view?usp=sharing
I already tried to change the insets of the tableview but it didn't work.
tableView.contentInset = UIEdgeInsets(top: navigationController?.navigationBar.height, left: 0, bottom: 0, right: 0)
Thanks!
As you discovered, to make large title fonts work as you want them to, the UIScrollView or any of its subclass needs to be the first element in the view hierarchy.
To fix your problem, you can try setting the background image to be the background of the UITableView directly.
Edit: Okay soo according to your comment you want a background behind everything including navigation bar. There is one way of achieving this and that is to subclass your UINavigationController and inside viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
let image = UIImage(named: "wallpaper")
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFill
imageView.frame = UIScreen.main.bounds
//this below part is important. Make sure to set it to 0 otherwise it will cover everything else
view.insertSubview(imageView, at: 0)
}
And then make sure your UIViewController containing the UITableView has a clear color for the UIView and remove the background image from that UIViewController
I'm adding a custom titleView inside a navigation bar using navigationItem.titleView for both Master/Detail VC. On changing the orientation of the device to landscape, titleView under MasterViewController works fine, but for DetailViewController titleView disappears. On changing the orientation back to portrait titleView appears back for DetailViewController. I have also attached a link for source code and video.
Is this an intended behavior or am I making a mistake from my side or is it an issue from Apple's side ?
//Custom Title View:
class TitleView: UIView {
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: 50, height: 20)
}
}
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Adding titleView for Master/Detail VC:
navigationItem.titleView = {
//Setting frame size here, did not make any difference
let view = TitleView(frame: .zero)
view.backgroundColor = .red
return view
}()
}
}
Full source code here: https://github.com/avitron01/SplitViewControllerIssue/tree/master/MathMonsters
Video highlighting the issue:
https://vimeo.com/336288580
I had the same issue. It seems an iOS bug. My workaround was to reassign the title view on every view layout. I used this piece of code in my DetailViewController:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if let v = navigationItem.titleView {
navigationItem.titleView = nil
navigationItem.titleView = v
}
}
For those who stumble upon this, see also iOS 11 navigationItem.titleView Width Not Set. Basically, there's two suggested workarounds:
use a custom UIView that tells iOS to treat its intrinsicContentSize to be as big as possible with UIView.layoutFittingExpandedSize
use widthAnchor/heightAnchor constraints to set width and height of your view
Programmatically i have created a View and added a label and buttons to it. its fine in vertical it aligns to centre but as i rotate the screen it does not aligns to centre rather it seems as left aligned.
This is my code:
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 150))
headerView.layer.borderWidth = 1
headerView.layer.borderColor = UIColor.whiteColor().CGColor
headerView.backgroundColor = ClientConfiguration.primaryUIColor()
let myLabel = UILabel()
myLabel.frame = CGRectMake(-1, -1, tableView.frame.width, 30)
myLabel.font = UIFont.boldSystemFontOfSize(14)
myLabel.backgroundColor = ClientConfiguration.primaryUIColor()
myLabel.textColor = UIColor.whiteColor()
myLabel.text = "Select Time Zone"
myLabel.textAlignment = .Center
let frame = CGRectMake(1, 1,headerView.frame.width , 70)
self.btnTimeZone.frame = frame
headerView.addSubview(myLabel)
headerView.addSubview(self.btnTimeZone)
self.buttonTitileString = self.selectedZone.value
self.btnTimeZone.setTitle(buttonTitileString, forState: .Normal)
return headerView
}
In horizontal mode the button text and label are aligned to left
and when i set self.btnTimeZone.contentHorizontalAlignment = UIControlContentHorizontalAlignment.Center
its fine in horizontal mode but in vertical mode they all are right aligned as;
enter image description here
How can i solve this issue i need both of them in centre aligned in both horizontal and vertical mode.
You just need to reload the table when rotate the phone. Implement following method and inside of this method reload the tableView
override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
tableView.reloadData()
}
In case of the application should support both portrait and landscape modes, the good approach would be to work with NSLayoutConstraint.
Of course you have both options to create them programmatically or from the Interface Builder, I'd like to note that you are able to create the header view as a cell, without the need of doing it programmatically; That's -obviously- will leads to the ease of setup the desired constrains for the header subviews.
Try this also
change
tableView.frame.width
to
tableView.frame.size.width
You could try to set autoresizing for parent view.
headerView.autoresizesSubviews = true
myLabel.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.btnTimeZone.autoresizingMask = [.flexibleWidth, .flexibleHeight]
In Swift 5 (and 3, 4 too) you got to override viewDidLayoutSubviews() method of life cycle of view controller, it calls after viewWillTransition() method when rotating your device.
In viewDidLayoutSubviews() method you just need to reload the data of your tableView in order to redraw your section header for portrait to landscape or landscape to portrait, and it works perfectly.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.tableView.reloadData()
}
Thanks to GayashanK for give me this idea with his answer.
What I've heard there are no real way) to set a background image for a view in swift (tex self.view.background image. After googling around I found this:
var imageView = UIImageView(frame: self.view.frame) // set as you want
var image = UIImage(named: "fade bg3")!
imageView.image = image
self.view.addSubview(imageView)
self.view.sendSubviewToBack(imageView)
It worked but the statusbar (with the clock, battery status and network ex) has that background too. Can I prevent this?
This was the solution
UIImageView(frame: CGRectMake(self.view.frame.origin.x, 20, self.view.frame.size.width, self.view.frame.size.height))
Just Hide your Status bar for those screens.
You really should implement prefersStatusBarHidden on your view controller(s):
override func prefersStatusBarHidden() -> Bool {
return true
}