I am new to iOS development. I encountered the problem, when I rotate the screen on iPhone X emulator I got white stripes on the side of the screen as you see on the second picture.
I have already set background for ViewController and for TableView.
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Colors.backGround
view.tintColor = Colors.backGround
It did not help. When the app starts from landscape mode the issue disappears.
Your blue view area has leading and trailing constraint to safe area.
Just make it to superview.
EDIT:
Here is code for same to achieve with swift code
//Creating tableview
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.backgroundColor = .lightGray
tableView.dataSource = self
self.view.addSubview(tableView)
//Setting layout of tableview
tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
//Setting insets for respect safe area
tableView.contentInset = self.view.safeAreaInsets
Here are screenshots for same.
Vertical
Horizontal
If you created UITableView Programmatically
then follow the below steps
private let tableView = UITableView()
then
func setupView() {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
}
then
func setupConstraints() {
self.tableView.topAnchor.constraint(equalTo: (self.tableView.superview.safeAreaLayoutGuide.topAnchor), constant: 0).isActive = true
self.tableView.leadingAnchor.constraint(equalTo: (self.tableView.superview.leadingAnchor), constant: 0).isActive = true
self.tableView.trailingAnchor.constraint(equalTo: (self.tableView.superview.trailingAnchor), constant: 0).isActive = true
self.tableView.bottomAnchor.constraint(equalTo: (self.tableView.superview.safeAreaLayoutGuide.bottomAnchor), constant: 0).isActive = true
}
Change your leading and trailing (or left and right) constraints for tableView from safe area to superview and the table will fill the screen horizontally.
Alternatively, just set the background color of the white view to that of the table view. That will ensure your table view content is always visible because it will stay inside the safe area (I'm assuming the white view is self.view here):
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = tableView.backgroundColor
}
Related
I'm trying to learn to build views without storyboard. I tried to build a scrollview. On that scrollview is a UISearchBar, a UIImageView with an image and a UILabel. It works but none of the content moves. The content is all just frozen in place like no matter how far I scroll the search bar will always be on top of the page. and the image on the bottom. I've attached a video to show what I mean. There's also a problem because none of the content is where I want it to be but that's another problem. I realize this is probably because I don't know enough about constraints and autolayout and building views without storyboards.
Here's the video
class HomePageViewController: UIViewController {
var searchedText: String = ""
let label = UILabel()
let searchBar: UISearchBar = {
let searchBar = UISearchBar()
searchBar.placeholder = "Where are you going?"
searchBar.translatesAutoresizingMaskIntoConstraints = false
searchBar.barTintColor = .systemCyan
searchBar.searchTextField.backgroundColor = .white
searchBar.layer.cornerRadius = 5
return searchBar
}()
let homeImage: UIImageView = {
let homeImage = UIImageView()
homeImage.translatesAutoresizingMaskIntoConstraints = false
homeImage.clipsToBounds = true
return homeImage
}()
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .systemMint
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height * 30)
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPink
// setupLayout()
// tried this here doesn't do anything for me
}
func setupLayout() {
view.addSubview(scrollView)
self.scrollView.addSubview(searchBar)
homeImage.image = UIImage(named: "Treehouse")
self.scrollView.addSubview(homeImage)
label.text = "Inspiration for your next trip..."
self.scrollView.addSubview(label)
// not sure where this label is being added I want it to be underneath the image but it isn't t
let safeG = view.safeAreaLayoutGuide
let viewFrame = view.bounds
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: -10),
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor),
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
searchBar.topAnchor.constraint(equalTo: safeG.topAnchor, constant: 50.0),
searchBar.widthAnchor.constraint(equalTo: safeG.widthAnchor, multiplier: 0.9),
searchBar.centerXAnchor.constraint(equalTo: safeG.centerXAnchor),
homeImage.topAnchor.constraint(equalTo: safeG.topAnchor, constant: 150),
homeImage.widthAnchor.constraint(equalTo: safeG.widthAnchor, multiplier: 1.1),
homeImage.centerXAnchor.constraint(equalTo: safeG.centerXAnchor),
homeImage.heightAnchor.constraint(equalToConstant: viewFrame.height/2),
label.topAnchor.constraint(equalTo: homeImage.bottomAnchor, constant: 100)
])
// was doing all this in viewDidLayoutSubviews but not sure if this is better place for it
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
setupLayout()
// tried this in viewDidLoad() and it didn't solve it.
}
}
any help would be appreciated
First, when constraining subviews in a UIScrollView, you should constrain them to the scroll view's Content Layout Guide. You're constraining them to the view's safe area layout guide, so they're never going to go anywhere.
Second, it's difficult to center subviews in a scroll view, because the scroll view can scroll both horizontally and vertically. So it doesn't really have a "center."
You can either put subviews in a stack view, or, quite common, use a UIView as a "content" view to hold the subviews. If you constrain that content view's Width to the scroll view's Frame Layout Guide width, you can then horizontally center the subviews.
Third, it can be very helpful to comment your constraints, so you know exactly what you expect them to do.
Here's a modified version of your posted code:
class HomePageViewController: UIViewController {
var searchedText: String = ""
let label: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let searchBar: UISearchBar = {
let searchBar = UISearchBar()
searchBar.placeholder = "Where are you going?"
searchBar.translatesAutoresizingMaskIntoConstraints = false
searchBar.barTintColor = .systemCyan
searchBar.searchTextField.backgroundColor = .white
searchBar.layer.cornerRadius = 5
return searchBar
}()
let homeImage: UIImageView = {
let homeImage = UIImageView()
homeImage.translatesAutoresizingMaskIntoConstraints = false
homeImage.clipsToBounds = true
return homeImage
}()
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .systemMint
// don't do this
//scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height * 30)
return scrollView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPink
setupLayout()
}
func setupLayout() {
view.addSubview(scrollView)
//homeImage.image = UIImage(named: "Treehouse")
homeImage.image = UIImage(named: "natureBKG")
label.text = "Inspiration for your next trip..."
// let's use a UIView to hold the "scroll content"
let contentView = UIView()
contentView.translatesAutoresizingMaskIntoConstraints = false
// give it a green background so we can see it
contentView.backgroundColor = .green
contentView.addSubview(searchBar)
contentView.addSubview(homeImage)
contentView.addSubview(label)
scrollView.addSubview(contentView)
let safeG = view.safeAreaLayoutGuide
let svContentG = scrollView.contentLayoutGuide
let svFrameG = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
// constrain scrollView to all 4 sides of view
// (generally, constrain to safe-area, but this is what you had)
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor),
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
// constrain contentView to all 4 sides of scroll view's Content Layout Guide
contentView.topAnchor.constraint(equalTo: svContentG.topAnchor, constant: 0.0),
contentView.leadingAnchor.constraint(equalTo: svContentG.leadingAnchor, constant: 0.0),
contentView.trailingAnchor.constraint(equalTo: svContentG.trailingAnchor, constant: 0.0),
contentView.bottomAnchor.constraint(equalTo: svContentG.bottomAnchor, constant: 0.0),
// constrain contentView Width equal to scroll view's Frame Layout Guide Width
contentView.widthAnchor.constraint(equalTo: svFrameG.widthAnchor),
// constrain searchBar Top to contentView Top + 50
searchBar.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 50.0),
// constrain searchBar Width to 90% of contentView Width
searchBar.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.9),
// constrain searchBar centerX to contentView centerX
searchBar.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// constrain homeImage Top to searchBar Bottom + 40
homeImage.topAnchor.constraint(equalTo: searchBar.bottomAnchor, constant: 40.0),
// constrain homeImage Width equal to contentView Width
homeImage.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 1.0),
// constrain homeImage centerX to contentView centerX
homeImage.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// constrain homeImage Height to 1/2 of scroll view frame Height
homeImage.heightAnchor.constraint(equalTo: svFrameG.heightAnchor, multiplier: 0.5),
// you probably won't get vertical scrolling yet, so increase the vertical space
// between the homeImage and the label by changing the constant
// from 100 to maybe 400
// constrain label Top to homeImage Bottom + 100
label.topAnchor.constraint(equalTo: homeImage.bottomAnchor, constant: 100.0),
// constrain label centerX to contentView centerX
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
// constrain label Bottom to contentView Bottom - 20
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20.0),
])
}
}
I have a contentview (Defined in an xib file) which has some labels and two buttons in it (white view on screenshot 1.a). If we came how It is shown on the screen, let me draw the scheme;
ViewController View -> View (Custom View Class) -> UIScrollView -> ContentView (Xib file)
Buttons are placed related to bottom margin. They both placed to bottom. Top button has a constraint which has 10 constant related to bottom of last label (This is greater than equal constraint and has UILayoutPriority of 5).
Problem: When I give constraint from button to label even It is greater than equal, button stacks just 10 point below to label. Even there is so much gap in bottom. (Screenshot 2.a)
What I want to achieve: I want that If all contents are fit on the screen with minimum 10 constraint from last label to first button, don't scroll. If not, scroll.
Custom View Class:
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
func initialize() {
self.translatesAutoresizingMaskIntoConstraints = false
let name = String(describing: type(of: self))
let nib = UINib(nibName: name, bundle: .main)
nib.instantiate(withOwner: self, options: nil)
cancelButton.layer.borderColor = UIColor.darkGray.cgColor
cancelButton.layer.borderWidth = 1.0
scrollView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.scrollView)
scrollView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
scrollView.addSubview(contentView)
self.contentView.translatesAutoresizingMaskIntoConstraints = false
//self.contentView.clipsToBounds = true
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
}
Example Screenshots:
If content fit into view, I want like below image. If not button can go up till It fits, If It doesn't fit even though, View should be scrollable.
Screenshot 2.a
The following line should be enough to calculate the height of the contents inside the scroll view and give you the proper behavior you expect.
contentView.heightAnchor.constraint(greaterThanOrEqualTo: scrollView.heightAnchor).isActive = true
I have properly set up an Viewcontroller containing an UIView with class "MGLMapView", what I am trying add an button to the Viewcontroller, but when I run the app the button does not show.
Same if I do an external Viewcontroller and overlay the MapViewController, it runs, but it does not show.
Why is that? have anybody had a similar problem? and how do I solve it?
Thanks in advance!
I don't know about the MGLMapView but it should adhere to same rules as any other UIView object.
This example is based on a MKMapView but it should be analog for MGMapView. Put this code in your VC and add setUpViews() to your viewDidLoad() method.
Note: this code should put the MKMapView at the bottom of the view stack and the map view should take up the entire view (except the top 64 pts--I assumed a UINavigationView might be in use--if not just change 64.0 to 0.0 in the constraints). Then it adds a UIButton on top of the MKMapView centered in the x direction with a width of 150 a height of 40 and 20 from the bottom of the view.
let map: MKMapView = {
let view = MKMapView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let button: UIButton = {
let view = UIButton()
// Add or change attributes as you need
view.backgroundColor = UIColor.green
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
fileprivate func setUpViews(){
// Add the map first
self.view.addSubview(map)
// Add the button now it will be on top of the map view
self.view.addSubview(button)
map.delegate = self;
NSLayoutConstraint.activate([
map.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 64.0),
map.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
map.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
map.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)
])
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor, constant: 0.0),
button.heightAnchor.constraint(equalToConstant: 40.0),
button.widthAnchor.constraint(equalToConstant: 150.0),
button.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -20.0),
])
}
I have a view controller with XIB, with a view (contentView) inside. This view contains some buttons.
The content view has round corners and clips to bounds, but it doesn't respect the clipping rect. I set the corner radius and the clipsToBounds in the viewDidLoad of the view controller.
Here you can see the reveal screenshot that shows that the view is composed in the correct way, but on simulator and device clipping bounds are not respected.
Anybody can please help me to understand what happen.
The app is targeted to iOS 10 and 11, and both have the same issue.
I found a solution, I move the clipsToBound in the viewDidLayoutSubviews instead viewDidLoad and now works
override func viewDidLoad() {
super.viewDidLoad()
contentView.layer.cornerRadius = Dimensions.CornerRaius
contentView.dropShadow()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
contentView.clipsToBounds = true
}
I defined my view (UIView in my case) like that:
fileprivate let backView: UIView = {
let view = UIView()
view.clipsToBounds = true
view.layer.masksToBounds = false
view.layer.cornerRadius = 10
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
The image:
fileprivate let imgView: UIImageView = {
let iv = UIImageView()
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
In my case I defined these elements in a custom table view cell:
class customCell: UITableViewCell {
Although I set "clipsToBounds = true" in the definition of background view, not clip the image.
But, if I set "clipsToBounds = true" later, it clips the image.
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
backView.addSubview(imgView)
contentView.addSubview(backView)
imgView.topAnchor.constraint(equalTo: backView.topAnchor, constant: 0).isActive = true
imgView.leadingAnchor.constraint(equalTo: backView.leadingAnchor, constant: 0).isActive = true
imgView.trailingAnchor.constraint(equalTo: backView.trailingAnchor, constant: 0).isActive = true
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: 1/4).isActive = true
backView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0).isActive = true
backView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15).isActive = true
backView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15).isActive = true
backView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0).isActive = true
And inside "init" method:
backView.clipsToBounds = true
So here is my current code for the viewdidload and the setup view func
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(bearImageView)
view.addSubview(descriptionText)
view.addSubview(startButton)
setupView()
}
#objc private func start() {
}
private func setupView() {
bearImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
bearImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
bearImageView.widthAnchor.constraint(equalToConstant: 200).isActive = true
bearImageView.heightAnchor.constraint(equalToConstant: 250).isActive = true
descriptionText.topAnchor.constraint(equalTo: bearImageView.bottomAnchor, constant: 10).isActive = true
descriptionText.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
descriptionText.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
startButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
startButton.topAnchor.constraint(equalTo: descriptionText.bottomAnchor, constant: 140).isActive = true
startButton.widthAnchor.constraint(equalToConstant: 80).isActive = true
startButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
}
Both the bearimage and button constraints work fine (minus flipping the phone horizontally but ill fix that later) however the text just refuses to show. The text are made programmatically for istance let descriptionText = UITextView = {...}() and etc. Any of you guys have an idea?
If you look closely you have missed the Height Constraint for your UITextView. If you're using a UILabel or UITextField they don't need a height constraint and can calculate their height based on it's inner contents but UITextView is not going to do that because it will start scrolling if the contents is more than it's height and that's why it can not set the height based on it's inner contents and it's height is zero by default.
add a HeightConstraint to your UITextView as well.
// This will fix your problem
descriptionText.heightAnchor.constraint(equalToConstant: 120).isActive = true
It's possible the image's intrinsic content size is so large that it is expanding such that there is no more space available for the descriptionText label. Try updating the content compression resistance priority of the label to required so it cannot be compressed by the image view.
Swift 3:
descriptionText.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .vertical)
Swift 4:
descriptionText.setContentCompressionResistancePriority(.required, for: .vertical)