My iOS app, built with UIKit, has this gray area at the top and I don't know how to make it go away. Here is the code for this screen:
app screenshot
import Foundation
import UIKit
class HomeController: UIViewController {
//MARK: - Properties
private let topStack = HomeNavigationStackView()
private let bottomStack = BottomControlsStackView();
private let deckView: UIView = {
let view = UIView()
view.backgroundColor = .systemPink
view.layer.cornerRadius = 5
return view
}()
//MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
configureCards()
}
//MARK: - Helpers
func configureCards(){
let user1 = User(name: "Jane Doe", age: 22, images: [#imageLiteral(resourceName: "jane1"),#imageLiteral(resourceName: "appSampleProfile")])
let user2 = User(name: "Megan", age: 34, images: [#imageLiteral(resourceName: "kelly2"),#imageLiteral(resourceName: "appSampleProfile")])
let cardView1 = CardView(viewModel: CardViewModel(user: user1))
let cardView2 = CardView(viewModel: CardViewModel(user: user2))
deckView.addSubview(cardView1)
deckView.addSubview(cardView2)
cardView1.fillSuperview()
cardView2.fillSuperview()
}
func configureUI(){
view.backgroundColor = .white
let stack = UIStackView(arrangedSubviews: [topStack, deckView, bottomStack])
stack.axis = .vertical
view.addSubview(stack)
stack.anchor(top:view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, right: view.rightAnchor)
stack.isLayoutMarginsRelativeArrangement = true
stack.layoutMargins = .init(top: 0, left: 12, bottom: 0, right: 12)
stack.bringSubviewToFront(deckView)
}
}
And here is the code for the navigation items at the top:
import UIKit
class HomeNavigationStackView: UIStackView {
// MARK: - Properties
let settingsButton = UIButton(type: .system)
let messageButton = UIButton(type: .system)
let tinderIcon = UIImageView(image: #imageLiteral(resourceName: "app_icon"))
//MARK: - Lifecycle
override init(frame: CGRect){
super.init(frame: frame)
heightAnchor.constraint(equalToConstant: 80).isActive = true
tinderIcon.contentMode = .scaleAspectFit
settingsButton.setImage(#imageLiteral(resourceName: "top_left_profile").withRenderingMode(.alwaysOriginal), for: .normal)
messageButton.setImage(#imageLiteral(resourceName: "top_right_messages").withRenderingMode(.alwaysOriginal), for: .normal)
[settingsButton, UIView(), tinderIcon, UIView(), messageButton].forEach{
view in addArrangedSubview(view)
}
distribution = .equalCentering
isLayoutMarginsRelativeArrangement = true
layoutMargins = .init(top:0, left:16, bottom:0, right:16)
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
you have embedded your view controller to a NavigationController in order to perform segues. You can hide the navigationBar and set your own Items like this:
navigationController?.navigationBar.tintColor = UIColor.clear
navigationController?.navigationBar.isHidden = false
navigationController?.navigationBar.backgroundColor = UIColor.clear
Or if you want to leave it and the thing that bothers you is the shadow and gray color then you can use this code. It removes the shadow on the bottom of navigationBar (Gray Bar) and sets the backGrounColor to clear, but it leaves you ability to set the titleColor, tile etc.
view.backgroundColor = UIColor.white
navigationController?.navigationBar.tintColor = UIColor.black
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.backgroundColor = UIColor.clear
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
Related
I´ve tried to change background color inside class SearchBarView: UIView {}:
searchBar.searchTextField.backgroundColor = .clear
searchBar.backgroundColor = .clear
and tryed something like that inside MainViewController:
searchBar.searchTextField.backgroundColor = .clear
searchBar.backgroundColor = .clear
searchBar.layer.backgroundColor = UIColor.clear.cgColor
but, unfortunately I still see this lines inside my custom searchBar.
How can I get rid of these lines?
My SearchBarView class:
class SearchBarView: UIView {
lazy var searchBar = createSearchBar()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(searchBar)
searchBar.snp.makeConstraints { make in
make.leading.equalTo(32)
make.centerY.equalToSuperview()
make.height.equalTo(34)
make.width.equalTo(300)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
fileprivate extension SearchBarView {
private func createSearchBar() -> UISearchBar {
let searchBar = UISearchBar()
searchBar.placeholder = " Search"
searchBar.searchTextField.font = UIFont(name: "MarkPro", size: 15)
searchBar.searchTextField.backgroundColor = .clear
let textFieldInsideSearchBar = searchBar.value(forKey: "searchField") as? UITextField
let imageV = textFieldInsideSearchBar?.leftView as! UIImageView
imageV.image = imageV.image?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
imageV.tintColor = UIColor(hexString: "FF6E4E")
return searchBar
}
}
My MainViewController class:
class MainViewController: UIViewController {
private var searchBarView: SearchBarView!
override func viewDidLoad() {
super.viewDidLoad()
setupSearchBarView()
}
private func setupSearchBarView() {
searchBarView = SearchBarView(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
view.addSubview(searchBarView)
searchBarView.searchBar.clipsToBounds = true
searchBarView.searchBar.layer.cornerRadius = 17
searchBarView.searchBar.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
searchBarView.searchBar.searchTextField.clipsToBounds = true
let directionalMargins = NSDirectionalEdgeInsets(top: 0, leading: 24, bottom: 0, trailing: 0)
searchBarView.searchBar.directionalLayoutMargins = directionalMargins
searchBarView.snp.makeConstraints { make in
make.leading.equalToSuperview()
make.top.equalTo(categoriesView.snp.bottom)
make.trailing.equalToSuperview()
make.height.equalTo(60)
}
}
}
If you want to make the top and bottom border lines on the textfield disappear (the dark gray ones), you will want to tweak the text field's border properties rather than the background colors. Try something like this:
searchBar.searchTextField.layer.borderWidth = 0
or
searchBar.searchTextField.layer.borderColor = UIColor.clear.cgColor
and adapt it to fit how you've set up the relevant subviews in your custom search bar.
Set the searchBar background image to empty. This eliminates all background issues you may have such as unwanted lines. For more info reference Apple docs: https://developer.apple.com/documentation/uikit/uisearchbar/1624276-backgroundimage
searchBar.backgroundImage = UIImage()
I was working on a live playground project and I created everything programmatically but I am not able to align my buttons in the stackView to centre please help me
here is code for the main playground
import PlaygroundSupport
import UIKit
let vc = HomeViewController()
let navController = UINavigationController(rootViewController: vc)
navController.view.frame = CGRect(x: 0, y: 0, width: 600, height: 600)
PlaygroundPage.current.liveView = navController
here is my code for HomeVC
import UIKit
public class HomeViewController:UIViewController{
let stackView:UIStackView = {
let st = UIStackView()
st.axis = .horizontal
st.alignment = .center
st.distribution = .fillEqually
st.backgroundColor = .cyan
st.spacing = 10
st.translatesAutoresizingMaskIntoConstraints = false
return st
}()
let generateButton:UIButton = {
let btn = UIButton()
btn.setTitle("Generate Array", for: .normal)
btn.backgroundColor = .yellow
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
let generateButton2:UIButton = {
let btn = UIButton()
btn.setTitle("Generate 2", for: .normal)
btn.backgroundColor = .brown
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
public override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemPink
view.addSubview(stackView)
stackView.addArrangedSubview(generateButton)
stackView.addArrangedSubview(generateButton2)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
stackView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 120).isActive = true
}
}
All I want is to align the buttons in that stack view to centre ..... please help me
Your buttons are centred correctly, UINavigationBar give you an illusion that they are not. To fix the issue, you have few options:
Hide navigation bar:
navigationController?.setNavigationBarHidden(true, animated: false)
Remove navigation bar translucency:
navigationController?.navigationBar.isTranslucent = false
Set edgesForExtendedLayout to an empty array (Source):
edgesForExtendedLayout = []
All these actions can be performed in viewDidLoad() function.
I'm trying to create a UIView and set it as a ViewController's view.
I did set it up but it won't show properly. for example, I chose a backgroundColor for the view, which is not showing. I added two buttons and they do show, but not as expected.
This is my UIView code:
import UIKit
public final class LoginView: UIView {
public override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(stackView)
self.backgroundColor = UIColor.appColors.white
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public let login: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont(name: "AvenirNext-Regular", size: 17.0)
button.setTitle("Login", for: .normal)
button.setTitleColor(UIColor.appColors.white, for: .normal)
button.backgroundColor = UIColor.appColors.green
button.contentHorizontalAlignment = .center
button.layer.cornerRadius = 10
button.layer.shadowColor = UIColor(white: 0, alpha: 0.25).cgColor
button.layer.shadowOffset = CGSize(width: 0, height: 2)
button.layer.shadowOpacity = 1
button.layer.shadowRadius = 0
button.layer.masksToBounds = false
button.clipsToBounds = true
return button
}()
public let signup: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont(name: "AvenirNext-Regular", size: 17.0)
button.setTitle("Signup", for: .normal)
button.setTitleColor(UIColor.appColors.white, for: .normal)
button.backgroundColor = UIColor.appColors.red
button.contentHorizontalAlignment = .center
button.layer.cornerRadius = 10
button.layer.shadowColor = UIColor(white: 0, alpha: 0.25).cgColor
button.layer.shadowOffset = CGSize(width: 0, height: 2)
button.layer.shadowOpacity = 1
button.layer.shadowRadius = 0
button.layer.masksToBounds = false
button.clipsToBounds = true
return button
}()
private lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [self.login, self.signup])
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.spacing = 10.0
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
}
This is my ViewController:
import UIKit
class loginVC: UIViewController {
override func loadView() {
super.loadView()
self.view = LoginVC()
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
As you can see, I did set up everything, and I did changed the look of the buttons and chose a backgorund color, but it just won't show on the ViewController.
I want to seperate the View and the ViewController because I really need flexability with this app, but it just won't work.
Any ideas?
In loadView(), it must be LoginView() instead of loginVC(), i.e
class loginVC: UIViewController {
override func loadView() {
super.loadView()
self.view = LoginView()
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Edit:
Try replacing UIColor.appColors with just UIColor and see if it giving the expected result. This is the only thing I changed in your code to get that working perfectly.
I want to add/set unread flag as red dot top right corner on UIbarButtonItem, See attached image for this
What should i do to add/set red dot on Bar Button item?
Once user tap on item then i want to remove red dot.
UIButton Subclass
Okay, let's start with creating custom UIButton subclass
class ButtonWithBadge: UIButton {
}
now let's create UIView for repsenting red dot
let badgeView: UIView = {
let view = UIView()
view.layer.cornerRadius = 3
view.backgroundColor = .red
return view
}()
Then override init for this subclass and inside add this badgeView to top right corner of your button: set its constraints (right and top equal to button's anchors and width and height to double of badgeView's cornerRadius value)
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(badgeView)
badgeView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
badgeView.rightAnchor.constraint(equalTo: rightAnchor, constant: 3),
badgeView.topAnchor.constraint(equalTo: topAnchor, constant: 3),
badgeView.heightAnchor.constraint(equalToConstant: badgeView.layer.cornerRadius*2),
badgeView.widthAnchor.constraint(equalToConstant: badgeView.layer.cornerRadius*2)
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Next create variable represeting current state of button:
var isRead: Bool = false
Now let's create some method which hide or unhide badgeView depending on isRead value
func setBadge() {
badgeView.isHidden = isRead
}
Now we have function, right? So let's call this function at the end of init and in didSet of isRead variable
class ButtonWithProperty: UIButton {
var isRead: Bool = false {
didSet {
setBadge()
}
}
override init(frame: CGRect) {
...
setBadge()
}
Adding To ViewController
First create variables for button and view
lazy var barButton: ButtonWithProperty = {
let button = ButtonWithProperty()
... // set color, title, target, etc.
return button
}()
now for example in viewDidLoad add this barButton to UINavigationBar and position it how you want to:
override func viewDidLoad() {
super.viewDidLoad()
...
guard let navigationBar = self.navigationController?.navigationBar else { return }
navigationBar.addSubview(barButton)
barButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
barButton.rightAnchor.constraint(equalTo: navigationBar.rightAnchor, constant: -20),
barButton.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: -6)
])
}
Now when you need to, you can just easily change barButton's isRead variable and red dot disappears or appears
barButton.isRead = true
class ButtonWithProperty: UIButton {
var isRead: Bool = false {
didSet {
setBadge()
}
}
lazy var badgeView: UIView = {
let view = UIView()
view.layer.cornerRadius = 3
view.backgroundColor = .red
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(badgeView)
badgeView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
badgeView.rightAnchor.constraint(equalTo: rightAnchor, constant: 3),
badgeView.topAnchor.constraint(equalTo: topAnchor, constant: 3),
badgeView.heightAnchor.constraint(equalToConstant: badgeView.layer.cornerRadius*2),
badgeView.widthAnchor.constraint(equalToConstant: badgeView.layer.cornerRadius*2)
])
setBadge()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setBadge() {
badgeView.isHidden = isRead
}
}
Inside ViewController:
class ViewController: UIViewController {
lazy var barButton: ButtonWithProperty = {
let button = ButtonWithProperty()
... // color, title, target, etc.
return button
}()
...
override func viewDidLoad() {
super.viewDidLoad()
...
guard let navigationBar = self.navigationController?.navigationBar else { return }
navigationBar.addSubview(barButton)
barButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
barButton.rightAnchor.constraint(equalTo: navigationBar.rightAnchor, constant: -20),
barButton.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor, constant: -6)
])
}
...
}
I've written a custom bar button class to handle this, where I'm using CAShapeLayer to draw a dot on top of the UIBarButtonItem.
// Custom Bar button
class CustomBarButton: UIBarButtonItem
{
// Unread Mark
private var unreadMark: CAShapeLayer?
// Keep track of unread status
var hasUnread: Bool = false
{
didSet
{
setUnread(hasUnread: hasUnread)
}
}
// Toggles unread status
private func setUnread(hasUnread: Bool)
{
if hasUnread
{
unreadMark = CAShapeLayer();
unreadMark?.path = UIBezierPath(ovalIn: CGRect(x: (self.customView?.frame.width ?? 0) - 10, y: 5, width: 5, height: 5)).cgPath;
unreadMark?.fillColor = UIColor.red.cgColor
self.customView?.layer.addSublayer(unreadMark!)
}
else
{
unreadMark?.removeFromSuperlayer()
}
}
}
There is no layer property available for bar button item, so you need to create your UIBarButtonItem using a custom view:
// Bar button property
var barButton:CustomBarButton!
// Initialisation
button = UIButton(type: .custom)
button.frame = CGRect(x: 0, y: 0, width: 70, height: 40)
button.setTitle("Right", for: .normal)
button.setTitleColor(UIColor.green, for: .normal)
// Bar button
barButton = CustomBarButton(customView: button)
button.addTarget(self, action: #selector(toggleStatus(sender:)), for: UIControl.Event.touchUpInside)
// Flexible space (Optional)
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
toolBar.items = [flexibleSpace, barButton]
And in the IBAction you can toggle the status using the hasUnread property:
#objc func toggleStatus(sender: AnyObject)
{
barButton.hasUnread = !barButton.hasUnread
}
And it will look like:
let dotView = UIView()
let btnSize =yourBarbutton.frame.size
let dotSize = 8
dotView.backgroundColor = .red //Just change colors
dotView.layer.cornerRadius = CGFloat(dotSize/2)
dotView.layer.frame = CGRect(x: Int(btnSize.width)-dotSize/2 , y:
dotSize, width: dotSize, height: dotSize)
yourBarbutton.addSubview(dotView)
I want to include a voting function similar to the reddit app. I think a UIStackView would be perfect for this. Now I'm struggling to make the label between the vote-up and vote-down button display it's text.
I've tried to change the contentCompression to .fittingSizeLevel and to .defaultHigh but this seems to change nothing. On the image you can see, there would be plenty of space to fit the whole text, but it doesn't. What aspect am I missing?
class VotingStackView: UIStackView {
let arrowUpButton: UIButton = {
let button = UIButton()
button.backgroundColor = .clear
button.tintColor = GSSettings.UI.Colors.tintColor
button.imageView?.contentMode = .scaleAspectFit
button.contentEdgeInsets = UIEdgeInsets(top: 16, left: 0, bottom: 16, right: 0)
button.clipsToBounds = true
return button
}()
let arrowDownButton: UIButton = {
let button = UIButton()
button.backgroundColor = .clear
button.tintColor = GSSettings.UI.Colors.tintColor
button.imageView?.contentMode = .scaleAspectFit
button.contentEdgeInsets = UIEdgeInsets(top: 16, left: 0, bottom: 16, right: 0)
button.clipsToBounds = true
return button
}()
let percentageLabel: UILabel = {
let label = UILabel()
label.text = "100%"
label.textColor = GSSettings.UI.Colors.regularTextColor
label.font = GSSettings.UI.Fonts.helveticaLight?.withSize(20)
label.clipsToBounds = false
label.setContentCompressionResistancePriority(UILayoutPriority.fittingSizeLevel, for: .horizontal)
return label
}()
var views: [UIView] = [UIView]()
//MARK: - Init & View Loading
override init(frame: CGRect) {
super.init(frame: frame)
views = [arrowUpButton, percentageLabel ,arrowDownButton]
setupStackView()
setupImages()
setupSubviews()
}
//MARK: - Setup
func setupStackView() {
self.axis = .horizontal
self.spacing = 0
self.alignment = .center
self.distribution = .fillEqually
}
func setupImages() {
let upImage = UIImage(named: "arrow_up")
let downImage = UIImage(named: "arrow_down")
arrowUpButton.setImage(upImage, for: .normal)
arrowDownButton.setImage(downImage, for: .normal)
}
func setupSubviews() {
for view in views {
addArrangedSubview(view)
}
layoutIfNeeded()
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The votingStackView is part of another StackView:
class BottomStackView: UIStackView {
let votingStackView: VotingStackView = {
let stackview = VotingStackView()
return stackview
}()
let addFriendButton: UIButton = {
let button = UIButton()
button.backgroundColor = .clear
button.tintColor = GSSettings.UI.Colors.tintColor
button.setImage(UIImage(named: "plus")?.withRenderingMode(.alwaysTemplate), for: .normal)
return button
}()
let redView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
var views: [UIView] = [UIView]()
//MARK: - Init & View Loading
override init(frame: CGRect) {
super.init(frame: frame)
views = [votingStackView, addFriendButton, redView]
setupStackView()
setupSubviews()
}
//MARK: - Setup
func setupStackView() {
self.axis = .horizontal
self.spacing = 0
self.alignment = .leading
self.distribution = .fillEqually
}
func setupSubviews() {
for view in views {
addArrangedSubview(view)
}
layoutIfNeeded()
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Change
label.setContentCompressionResistancePriority(UILayoutPriority.fittingSizeLevel, for: .horizontal)
to
label.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: .horizontal)
From the docs it seems that fittingSizeLevel does not take into account parent constraints, so using defaultHigh seems like the safer option.