I have a static tableViews and a button to change the colour scheme (theme) of the app. Is there a fast way for me to change all the label colours throughout the table from white to black?
im thinking something like the following.
Pseudo code:
for subview in view.subviews {
if subview is UILabel {
subview.fontColor = .black
}
}
So when the orange switch is on the "Light" side I would like all labels to go black. I have used story board to construct it, so it would be nice if I could avoid having to connect all labels to the .swift file.
I am writing about code for your sudo code you need to iterate with recursion.
func getLabelsInView(view: UIView) -> [UILabel] {
var results = [UILabel]()
for subview in view.subviews as [UIView] {
if let label = subview as? UILabel {
results += [label]
} else {
results += getLabelsInView(view: subview)
}
}
return results
}
Call any where from you need to change color
let labels = getLabelsInView(self.view) // or any other view / table view
for label in labels {
label.textColor = .black
}
Related
I have a stackview with three subviews:
stackView.spacing = 8.0
stackView.addArrangeSubview(view1)
stackView.addArrangeSubview(view2)
stackView.addArrangeSubview(view3)
At some point, I add custom spacing after view2:
stackView.setCustomSpacing(24.0, after: view2)
Now, I want to remove the custom spacing. Is this possible using UIStackView? The only way I can think of is
stackView.setCustomSpacing(8.0, after: view2)
but this will break if I ever change the spacing of my stackView to something other than 8.0, because spacing will remain 8.0 after view2.
You should re-create stackView with specific spacing and replace the old one with the new stackView.
Try this extension:
extension UIStackView {
// remove all custom spacing
func removeCustomSpacing() -> Void {
let a = arrangedSubviews
arrangedSubviews.forEach {
$0.removeFromSuperview()
}
a.forEach {
addArrangedSubview($0)
}
}
// remove custom spacing after only a single view
func removeCustomSpacing(after arrangedSubview: UIView) -> Void {
guard let idx = arrangedSubviews.firstIndex(of: arrangedSubview) else { return }
removeArrangedSubview(arrangedSubview)
insertArrangedSubview(arrangedSubview, at: idx)
}
}
Use it simply as:
myStackView.removeCustomSpacing()
or, if you are setting custom spacing on multiple views, and you want to remove it from only one view, use:
theStackView.removeCustomSpacing(after: view2)
let spacingView = UIView()
[View1, View2, spacingView, View3].forEach { view in
stackView.addArrangeSubview(view)
}
when you want to remove it just remove the spacingView from the array and you can play with width and height of the spacingView like you prefer
I want to customize SLComposeSheetConfigurationItem
I am looking to change the font and color for its title and value
There does not seem to be any documentation for this but several apps like Evernote have done that.
SLComposeSheetConfigurationItem's contents are loaded in tableView, so you can't directly access both label. You need to first get tableView by accessing hierarchy of views and then access visibleCells from table view. there are only one cell, so access subViews of contentView of first cell which will give you two label.
So, first one label is left hand side and second one at right hand side. Change its font size, color, title as we are doing in normal label and reload data.
See following code.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let slSheet = self.children.first as? UINavigationController {
// Change color of Cancel and Post Button
slSheet.navigationBar.tintColor = .orange
// All contents of botton view are loaded inside table view at specific level of child view
if let tblView = slSheet.children.first?.view.subviews.first as? UITableView,
let firstCell = tblView.visibleCells.first
{
// Enumerate on subViews of contentView
for (index, label) in firstCell.contentView.subviews.enumerated() {
if index == 0 {
// Left label
if let lblLeft = label as? UILabel {
lblLeft.font = UIFont(name: "Helvetica-Bold", size: 20)
lblLeft.textColor = .orange
}
} else {
// Right label
if let lblRight = label as? UILabel {
lblRight.font = UIFont(name: "Helvetica", size: 14)
lblRight.textColor = .green
}
}
}
// Reload table if label not loading properly
tblView.reloadData()
}
}
}
Output:
My app deployment target version is iOS 10. and I added navigation bar large title in my app. it is working as per need in above iOS 10. if I try to test this in iOS 10 it is not working. So I am trying to create custom Navigation bar large tile for iOS 10 as well. but i don't know how to achieve this. please guide me. Thanks Advance
if #available(iOS 11.0, *) {
navigationController?.navigationBar.prefersLargeTitles = true
} else {
// need to add here as well
}
In case somebody needs this. Here is how I did it. For me, this is better than the default, because it supports whatever customization you may want from large title (ex. multiline)
In my case my layout looks like this. You can have however you want, but make sure title is not inside of the table view / scroll view.
view
view
large title label
view (this view will stick on top)
view
table view
view
view
In this case, I have scrollViewDidScroll delegate, which checks the scrollView content offset to change the titleLabels top constraint. For me top constraint is 16. Change it to whatever you want to have
extension YourViewController: UITableViewDelegate {
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
let titleHeight = titleLabel.bounds.height
if (scrollView.contentOffset.y <= 0) {
// Title is fully visible - table view is at the top
titleLabelTopConstraint.constant = 16
isLargeTitleHidden = false
} else if (scrollView.contentOffset.y > (titleHeight + 16)){
// Title is not visible at all. Table view is at an unknown position but it is not top
titleLabelTopConstraint.constant = -titleHeight
isLargeTitleHidden = true
} else {
// Title is kind of visible. Not fully hidden or shown.
titleLabelTopConstraint.constant = -scrollView.contentOffset.y + 16
isLargeTitleHidden = false
}
} }
I also have the isLargeTitleHidden to update the nav
var isLargeTitleHidden: Bool = false {
didSet{
if (oldValue != isLargeTitleHidden){
updateNavBar()
}
}
}
func updateNavBar(){
let fadeTextAnimation = CATransition()
fadeTextAnimation.duration = 0.2
fadeTextAnimation.type = CATransitionType.fade
navigationController?.navigationBar.layer.add(fadeTextAnimation, forKey: "fadeText")
if isLargeTitleHidden {
navigationItem.title = titleLabel.text
} else {
navigationItem.title = ""
}
}
NavigationBar have a titleView object where the title is. You can customize a label to go there however you want and make navigationBar.titleView = yourLabel or make a custom UIView all the same.
So I am in a mildly complicated situation,
The issue: There is a thin blue line that goes across my Custom UITableViewSectionHeader only when under navigation bar that I do not know where is coming from:
I have:
A tableview nested inside a UIViewController
A GradientView (inherits UIView) directly under my NavigationBar
A TableView that overlaps my GradientView
A Custom TableViewSectionHeader Class (Subclass UITableViewCell)
[
My theory:
That border line is either from:
- The bottom border of navigation bar
- The top border of tableview
- Maybe a separator from the Section header (but seems unlikely)
- A bottom border from the GradientView
Anybody have an idea what could be causing that line?
I have tried to remove it with:
ViewController:
override func viewDidLoad() {
// self.tableview.separatorStyle = .none
// self.tableview.layer.borderWidth = 0
// self.view.layer.borderWidth = 0
}
GradientView:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderWidth = 0
}
SectionHeader:
self.separatorInset.left = 1000
self.layer.borderWidth = 0
Any thoughts?
Navigation bars has an image view with a line which is somewhere between less than or equals to 1px you have to loop through NavigationController navigationBar to find that imageView and set that to hidden.
You can directly loop on navigationBar and find all subViews or if you want to have a reference to the view then here how I would have done it.
var lineImageView: UIImageView? = { [unowned self] in
// guard is great try to use it whenever you can
guard let navigationBar = self.navigationController?.navigationBar else {
return nil
}
return self.findLineImageView(for: navigationBar)
}()
now add this function which loops through till it finds an imageView and return it back to our lineImageView
// remember even **navigationBar** is a UI remember **UINavigationBar**
func findLineImageView(for view: UIView) -> UIImageView? {
// as I said above the line is not more than 1px so we look for a view which is less than or equals to 1px in height
if view is UIImageView && view.bounds.size.height <= 1 {
return (view as! UIImageView)
}
// we loop till we find the line image view and return it
for subview in view.subviews {
if let imageView = findLineImageView(for: subview) {
return imageView
}
}
// if there is no imageView with that height we return nil that's why we return an optional UIImageView
return nil
}
Now the magic part. in viewWillApear set the lineImageView to hidden
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// remember as I said the lineImageView we returned an optional that's why it has question mark which means we are safe
lineImageView?.isHidden = true
}
I'm working on my view and I'm having an issue with getting a shadow around a button within the stack view. Most of the work I have done has been within the storyboard directly.
Here is the method I am using to apply the shadow to the view
func addShadow(to view: UIView) {
view.layer.shadowColor = shadowColor
view.layer.shadowOpacity = shadowOpacity
view.layer.shadowOffset = shadowOffset
if let bounds = view.subviews.first?.bounds {
view.layer.shadowPath = UIBezierPath(rect: bounds).cgPath
}
view.layer.shouldRasterize = true
}
and this is how I'm finding the button within the view from ViewController.swift
for subview in self.view.subviews {
if subview.isKind(of: UIButton.self) && subview.tag == 1 {
addShadow(to: subview)
}
}
I know the problem stems from the stack view and the UIView inside of the stack view that holds the button. (self.view > UIStackView > UIView > [UIButton, UILabel])
I know I could do this with recursion in the for-loop but I'm trying to be a little more precise to optimize performance and would prefer to add the shadows in one shot.
You have a few options:
add the shadow in the storyboard itself
add an outlet to the button, then add shadow in code
add the button to a collection, then enumerate over the collection adding shadows
recursively add the shadows (this isn't going to hit performance nearly as hard as you're thinking, adding the shadows hurts performance more than doing this recursively)
You are correct in that the button is a view on the stack view, so your for loop doesn't hit the button directly to add a shadow to it.
The easiest way to solve this is by far the recursive way, or something like this:
func addShadowsTo(subviews: [UIView]) {
for subview in subviews {
if subview.isKind(of: UIButton.self) && subview.tag == 1 {
addShadow(to: subview)
}
if let stackView = subview as? UIStackView {
addShadowToSubviews(subviews: stackView.subviews)
}
}
}
func viewDidload() {
super.viewDidLoad()
addShadowsTo(subviews: view.subviews)
}
If you want some instructions on how to do any of the other ways, just comment.