I would like to know in which function e.g viewDidLoad etc.. would be the best approach to load custom view properties like image border heights, color, all appearance.
For example I add custom values like these in cellForRowAtIndexPath which I think is not best way to do it:
// corner radius
cell.feedImageView.layer.cornerRadius = 10
// border
cell.feedImageView.layer.borderWidth = 2.0
cell.feedImageView.layer.borderColor = UIColor.black.cgColor
cell.feedImageView.layer.masksToBounds = true
And these navigationBar appearance I load in viewDidLoad and i use it in many views:
navigationController?.navigationBar.barTintColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
self.navigationItem.leftBarButtonItem = nil
navigationController?.navigationBar.isTranslucent = true
let fontDictionary = [ NSForegroundColorAttributeName:UIColor.white ]
self.navigationController?.navigationBar.titleTextAttributes = fontDictionary
self.navigationController?.navigationBar.tintColor = UIColor(red: 0.184, green: 0.996, blue: 0.855, alpha: 1.00)
So the question again, where should I load appearance values? I ask it because right now I am in situation where my image border is loading before image etc..
There are so many functions like viewDidLoadAppearance(), ViewDidload(). I've read many questions where they ask what is difference between them but I still do not know where is best to load appearance.
You have basically 3 different options.
1) You could get or set those values in the viewDidAppear() func, after they have been completely instantiated.
2) You could get or set them in the vieDidLayoutSubviews() function and set a bool to check if your task has been called.
var didConfigureUI = false
func viewDidLayoutSubviews() {
if !didConfigureUI {
didConfigureUI = true
updateUI()
}
}
3) You could get or set them in the viewDidLoad using layoutIfNeeded():
override func viewDidLoad() {
super.viewDidLoad()
xy.view.layoutIfNeeded() // <- This will update your initial view frames
updateUI()
}
And your func to do whatever:
func updateUI() {
//f.e feedImageView.layer.masksToBounds = true
}
And to do it in your cellForRowAtIndexPath, you would do something like this, using the layoutIfNeeded() as well.
cellForRowAtIndexPath {
let cell = tableView.blahblahcell
updateUICell(cell.feedImageView)
}
func updateUICell(imageView: UIImageView) {
imageView.layoutIfNeeded()
imageView.layer.cornerRadius = 10
imageView.layer.borderWidth = 2.0
imageView.layer.borderColor = UIColor.black.cgColor
imageView.layer.masksToBounds = true
}
And to answer the last question about the lifecircle:
The circle is:
init
UIViewController awakeFromNib
loadView // your pictureImageView is loaded here
UIView awakeFromNib
UIViewController viewDidLoad
viewWillAppear
viewDidAppear // the properties of your pictureImageView are available here
Related
I am using a UISegmentControl and am trying to give each segment's view a custom UIColor. This has been easy to do for solid colors. I simply do:
providerSegmentedControl.subviews[1].tintColor = UIColor.red
And the segment at index 1 will now be tinted with the color red.
However, I am trying to add a gradient for the tintColor of one of my segment's UIView. I have an image in my assets called "AppleMusicGradient" that looks like this:
I am trying to create a UIColor from this image using UIColor(patternImage:). The code looks like this:
//UIImage.resize is a UIImage extension function that simply resizes a UIImage to the given CGRect
let image = UIImage(named: "AppleMusicGradient")!.resize(targetSize: providerSegmentedControl.subviews[2].frame.size)
providerSegmentedControl.subviews[2].tintColor = UIColor(patternImage: image)
However, this method seems to only be partially working. When the given segment is selected, this gradient isn't visible at all. And when the given segment is unselected, the gradient is only visible on the label inside of the segment, i.e. the border normally seem is not present.
See the "Apple Music" segment.
My solution feels a little "hacky" but I believe this may be what you are looking for.
#IBOutlet weak var segmentedControl: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.updateGradientBackground()
}
private func updateGradientBackground() {
let sortedViews = segmentedControl.subviews.sorted( by: { $0.frame.origin.x < $1.frame.origin.x } )
for (index, view) in sortedViews.enumerated() {
if index == segmentedControl.selectedSegmentIndex {
view.backgroundColor = UIColor(patternImage: UIImage(named: "7Wk4B")!)
view.tintColor = UIColor.clear
let titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
UISegmentedControl.appearance().setTitleTextAttributes(titleTextAttributes, for: .selected)
} else {
view.backgroundColor = UIColor.white
view.tintColor = UIColor.blue
}
}
}
#IBAction func segmentedControllerTapped(_ sender: Any) {
self.updateGradientBackground()
}
When using a UISegmentedControl, I have a problem fitting all the labels, precisely in Japanese.
I did not notice the same issue with the other language I used at this point.
Here is my code:
interfaceChoice = UISegmentedControl(items: ["白黒モード", "緑赤青・モード"])
for i in 0..<interfaceChoice.subviews.count {
interfaceChoice.subviews[i].tintColor = localColor
for subSubView in interfaceChoice.subviews[i].subviews {
if subSubView is UILabel {
(subSubView as! UILabel).adjustsFontSizeToFitWidth = true
}
}
}
I would hope the line ..adjustsFontSizeToFitWidth.. would sort things out, but it does not work as one can see in the picture below.
Anyone has an idea as what I am doing wrong?
Storyboard example
This is how I did it.
In a story board, drag and drop a segmented control into the title area of the view controller.
Link the segmented control to an IBOutlet in the view controller.
This code
class ViewController: UIViewController {
#IBOutlet weak var interfaceChoice: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
interfaceChoice.removeAllSegments()
interfaceChoice.insertSegment(withTitle: "白黒モード", at: 0, animated: false)
interfaceChoice.insertSegment(withTitle: "緑赤青・モード", at: 1, animated: false)
interfaceChoice.selectedSegmentIndex = 0
let font = UIFont.boldSystemFont(ofSize: 16)
interfaceChoice.setTitleTextAttributes([NSFontAttributeName: font], for: .normal)
interfaceChoice.tintColor = UIColor(red: 27/CGFloat(255), green: 77/CGFloat(255), blue: 102/CGFloat(255), alpha: 1.0)
}
}
Note: I won't pretend to know exactly why it's not behaving for you, but I hope this can help. If it is absolutely imperative to fix your exact situation, I think we might need more information.
Pure code example
This produces the same result as setting it up in the storyboard.
class ViewController: UIViewController {
var interfaceChoice: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
interfaceChoice = UISegmentedControl(items: ["白黒モード", "緑赤青・モード"])
interfaceChoice.selectedSegmentIndex = 0
let font = UIFont.boldSystemFont(ofSize: 16)
interfaceChoice.setTitleTextAttributes([NSFontAttributeName: font], for: .normal)
interfaceChoice.tintColor = UIColor(red: 27/CGFloat(255), green: 77/CGFloat(255), blue: 102/CGFloat(255), alpha: 1.0)
self.navigationItem.titleView = interfaceChoice
}
}
I'm adding CALayer to top and bottom of scrollable objects (UIScrollView, TableView, CollectionView) to display them when there is a content behind the visible area.
class TableViewWithCALayers: UITableView {
var topGradientLayer: CAGradientLayer?
var bottomGradientLayer: CAGradientLayer?
override func layoutSubviews() {
super.layoutSubviews()
guard self.topGradientLayer != nil && self.bottomGradientLayer != nil else {
addGradientLayerToTop() // create layer, set frame, etc.
addGradientLayerToBottom()
return
}
// addGradientLayerToTop()// if uncomment it - multiple layers are created and they are visible, but this is not the solution...
handleLayerAppearanceAfterLayoutSubviews() // playing with opacity here
}
How I create layer:
func addGradientLayerToTop() {
if let superview = superview {
self.topGradientLayer = CAGradientLayer()
let colorTop = UIColor.redColor().CGColor
let colorBottom = UIColor.clearColor().CGColor
if let topLayer = self.topGradientLayer {
topLayer.colors = [colorTop, colorBottom]
topLayer.locations = [0.0, 1.0]
topLayer.frame = CGRect(origin: self.frame.origin, size: CGSizeMake(self.frame.width, self.layerHeight))
superview.layer.insertSublayer(topLayer, above: self.layer)
if (self.contentOffset.y == 0) {
// if we are at the top - hide layer
// topLayer.opacity = 0.0 //temporarily disabled, so it is 1.0
}
}
}
}
TableViewWithCALayers works nice everywhere, except using TableView with xib files:
class XibFilesViewController : CustomUIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: TableViewWithCALayers!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerNib(UINib(nibName: "CustomTableViewCell", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: "CustomTableViewCell")
self.tableView.layer.masksToBounds = false // this line doesn't help...
}
CustomUIViewController is used in many other ViewControllers where TableViewWithCALayers works good, so it should not create a problem.
Layers at the top and bottom appear for one second, then disappear. Logs from LayoutSubviews() func say that they are visible and opacity are 1.0, but something covers them. What can it be and how to deal with that?
Any help is appreciated!)
topLayer.zPosition = 10000 //doesn't help
topLayer.masksToBounds = false //doesn't help as well
When using nib files it's good practice, and design to add the UIView that you want to draw the layer on into your prototype cell, or header/footed and then have that view confirm to your class that's actually handling the layer.
I have a view (UIPopoverPresentation) which functionality works fine, but I need to add a custom border. I'm currently using a borderWidth and borderColor but I cannot seem to find a way to make a customized border, as seen in the photo below. How do I go about creating this customized border? Make a CGRect?
What I need:
What I have:
I've attempted to add an image to the background of the popover and it resulted in this:
EDIT: //PopOverView (presented using UIPopOverPresentation)
override func viewDidLoad() {
super.viewDidLoad()
self.view.layer.cornerRadius = 10.0
self.view.layer.borderWidth = 1.5
self.view.layer.borderColor = UIColor.whiteColor().CGColor
self.navigationController?.navigationBarHidden = true
self.popViewTableView.delegate = self
self.popViewTableView.dataSource = self
self.popViewTableView.alwaysBounceVertical = false
self.popViewTableView.backgroundColor = UIColor(red: 151.0/255.0, green: 87.0/255.0, blue: 172.0/255.0, alpha: 1.0)
}
//Base View Controller. When button is pressed, this function is called which presents the popover
func presentPopOver() {
let contentView = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("popViewController") as! DeckPopViewController
contentView.modalPresentationStyle = UIModalPresentationStyle.Popover
contentView.preferredContentSize = CGSizeMake(deckSelectionCGRect.width, 160)
let popoverMenuViewController = contentView.popoverPresentationController!
popoverMenuViewController.delegate = self
popoverMenuViewController.sourceView = view
popoverMenuViewController.permittedArrowDirections = UIPopoverArrowDirection(rawValue:0)
popoverMenuViewController.sourceRect = CGRectMake((self.view.bounds.width/2) - (deckSelectionCGRect.width/2), 120, deckSelectionCGRect.width, deckSelectionCGRect.height)
presentViewController(contentView, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
//ALSO: On the third image, I placed the border imageview over the entire view controller in the storyboard, set padding to zero on all sides (autolayout). It doesn't look good, though.
class mainTabBarViewController: UITabBarController, UITabBarControllerDelegate {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
tabBarController?.delegate = self
self.moreNavigationController.topViewController.view.backgroundColor = UIColor(red: 25.0/255.0, green: 25.0/255.0, blue: 25.0/255.0, alpha: 1.0)
let view = self.moreNavigationController.topViewController.view as UITableView
for cell in view.visibleCells(){
let tableCell = cell as UITableViewCell
tableCell.backgroundColor = UIColor(red: 25.0/255.0, green: 25.0/255.0, blue: 25.0/255.0, alpha: 1.0)
tableCell.textLabel?.textColor = UIColor.whiteColor()
}
}
func tabBarController(tabBarController: UITabBarController, willBeginCustomizingViewControllers viewControllers: [AnyObject]) {
let editView = tabBarController.view.subviews[1] as UIView
editView.backgroundColor = UIColor.redColor()
}
}
Here is my current code. I got the code that's in the 'willBeginCustomizingViewControllers' from an old website (around 4-5 years old) and it worked then apparently but it's not doing anything now. I want to change the background of the "Edit" modal view within the moreNavigationController to the same colour as i have set to the table cells in the viewWillAppear. I've put red for now just to test. Any ideas?
I've done some research, but i cant seem to find anything. I'm not sure its even possible.
There is one more option, but its not so easy. You can call your fifth tab 'more', and place a collection view inside it to access all of the other tabs (just copy apple's 'more' tab, but just start from scratch on a new view controller.).