UISearchBarDelegate methods not being called - ios

I had a working search bar and out of nowhere I stopped being able to even to activate any delegate methods. I created the search bar programmatically.
Here is my initialization code:
self.searchBar.delegate = self
self.searchBar.translatesAutoresizingMaskIntoConstraints = false
self.searchBar.searchBarStyle = .minimal
self.searchBar.isUserInteractionEnabled = true
let image = self.getImageWithColor(color: UIColor(red: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: 0.19), size: CGSize(width: self.customNavBar.frame.width * 0.9440, height: self.customNavBar.frame.height * 0.47727))
searchBar.setSearchFieldBackgroundImage(image, for: .normal)
self.customNavBar.addSubview(self.searchBar)
var horizCenter = NSLayoutConstraint(item: self.searchBar, attribute: .centerX, relatedBy: .equal, toItem: self.customNavBar, attribute: .centerX, multiplier: 1, constant: 0)
var vertConstraints = NSLayoutConstraint(item: self.searchBar, attribute: .top, relatedBy: .equal, toItem: self.customNavBar, attribute: .top, multiplier: 1, constant: 30)
var widthConstraint = NSLayoutConstraint(item: self.searchBar, attribute: .width, relatedBy: .equal, toItem: self.customNavBar, attribute: .width, multiplier: 0.9440, constant: 0)
var heightConstraint = NSLayoutConstraint(item: self.searchBar, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: self.customNavBar.frame.height * 0.47727 )
NSLayoutConstraint.activate([horizCenter, vertConstraints, widthConstraint, heightConstraint])
Here is the getImageWithColor method I referenced above:
func getImageWithColor(color: UIColor, size: CGSize) -> UIImage {
let rect = CGRect(x:0, y:0, width:size.width,height: size.height)
let path = UIBezierPath(roundedRect: rect, cornerRadius: 5.0)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
path.fill()
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
And I continue to customize the textField inside the searchBar:
UITextField.appearance().tintColor = UIColor.white
let cancelButtonAttributes: NSDictionary = [NSForegroundColorAttributeName: UIColor.white, NSFontAttributeName : UIFont(name: "Gotham-Book", size: 14.0)!]
UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes as? [String : AnyObject], for: UIControlState.normal)
let textFieldInsideSearchBar = self.searchBar.value(forKey: "searchField") as? UITextField
textFieldInsideSearchBar?.textColor = UIColor(red: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: 1.0)
textFieldInsideSearchBar?.font = UIFont(name: "Gotham Medium", size: 20.0)
textFieldInsideSearchBar?.clearsOnBeginEditing = true
textFieldInsideSearchBar?.borderStyle = .none
textFieldInsideSearchBar?.clearButtonMode = .whileEditing
textFieldInsideSearchBar?.isUserInteractionEnabled = true
And all my delegate methods:
extension ListViewController : UISearchBarDelegate {
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
print("\nfunc searchBarTextDidBeginEditing\n")
self.hideDayDropDown()
UIView.animate(withDuration: 0.25) {
self.dayButton.alpha = 0.0
self.dayButtonLabel.alpha = 0.0
self.dayDropDownArrow.alpha = 0.0
self.mapButton.alpha = 0.0
self.mapButton.isEnabled = false
self.dayButton.isUserInteractionEnabled = false
self.tableView.transform = CGAffineTransform(translationX: 0.0, y: -(self.tableView.frame.minY - self.customNavBar.frame.maxY))
self.tableView.translatesAutoresizingMaskIntoConstraints = true
self.tableView.frame = CGRect(x: 0, y: self.customNavBar.frame.maxY, width: self.view.frame.width, height: 300)
self.dealOfTheDayLabel.alpha = 0.00
self.exploreSpecialView.alpha = 0.0
self.exploreSpecialsLabel.alpha = 0.0
}
searchActive = true
self.searchBar.setShowsCancelButton(true, animated: true)
UIView.animate(withDuration: 0.25) {
self.searchIconImage.alpha = 0.0
self.searchIconWhileEditing.alpha = 1.0
}
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
print("\nfunc searchBarTextDidEndEditing\n")
UIView.animate(withDuration: 0.25) {
self.dayButton.alpha = 1.0
self.dayButtonLabel.alpha = 1.0
self.dayDropDownArrow.alpha = 1.0
self.dayButton.isUserInteractionEnabled = true
self.searchIconImage.alpha = 1.0
self.searchIconWhileEditing.alpha = 0.0
self.tableView.transform = CGAffineTransform(translationX: 0.0, y: 0.0)
self.tableView.translatesAutoresizingMaskIntoConstraints = false
self.mapButton.alpha = 1.0
self.mapButton.isEnabled = true
self.dealOfTheDayLabel.alpha = 1.0
self.exploreSpecialView.alpha = 1.0
self.exploreSpecialsLabel.alpha = 1.0
}
searchActive = false
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
print("\nfunc searchBarCancelButtonClicked\n")
self.searchBar.setShowsCancelButton(false, animated: true)
self.searchBar.text = ""
self.searchBar.resignFirstResponder()
searchActive = false
self.searchIconImage.alpha = 1.0
self.searchIconWhileEditing.alpha = 0.0
self.dayDropDownArrow.alpha = 1.0
self.tableView.transform = CGAffineTransform(translationX: 0.0, y: 0.0 )
self.dealOfTheDayLabel.alpha = 1.0
self.exploreSpecialView.alpha = 1.0
self.exploreSpecialsLabel.alpha = 1.0
tableView.reloadData()
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
self.searchResults = self.library.filter({ (bar: BarLibrary) -> Bool in
let tmp: String = bar.name!
let range = tmp.localizedLowercase.range(of: searchText.localizedLowercase)
return range != nil
})
if(self.searchResults.count == 0){
searchActive = false
} else {
searchActive = true
}
self.tableView.reloadData()
}
}
Here is also an image from my interface capture of my navigation bar with the search bar:
Screen Capture Image as seen in Interface Capture: View names from front to back: UITextFieldBorderView, UISearchBarTextField, UIView, UISearchBar
Note I'm not sure what the UIView right before the UISearchBar is for
I understand there are a lot of questions on SO about why others can't access their UISearchBar delegate methods. None of those solutions have worked for me. Why can't I access my delegate methods thus far?

If none of the delegate methods are working my guess is that your searchBarDelegate is being deallocated for some reason, it is hard to tell without seeing the whole code, but be sure to keep a strong reference to whoever is your searchBarDelegate, because the delegate itself is weak meaning if no one else is retaining it it could be deallocated and thus there is no delegate to respond to the calls.

Related

Empty UITableView with tableFooterView

I have a UITableView in which I want to show a message when the dataSource is empty. I do this with the well-known method of setting the backgroundView using the following extension:
extension UITableView {
func setEmptyMessage(_ message: String, _ image: String) {
let emptyView: UIView = {
let emptyView = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height))
return emptyView
}()
let contentView: UIView = {
let contentView = UIView()
contentView.translatesAutoresizingMaskIntoConstraints = false
return contentView
}()
let messageLabel = UILabel()
let messageCommentStyle = NSMutableParagraphStyle()
messageCommentStyle.lineHeightMultiple = 1.2
let attributedString = NSMutableAttributedString(string: message)
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: lightFeedUserNameFontColor, range: NSRange(location: 0, length: attributedString.length))
attributedString.addAttribute(NSAttributedString.Key.paragraphStyle, value: messageCommentStyle, range: NSRange(location: 0, length: attributedString.length))
attributedString.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: normalFontSize), range: NSRange(location: 0, length: attributedString.length))
messageLabel.attributedText = attributedString
messageLabel.numberOfLines = 0
messageLabel.font = UIFont.systemFont(ofSize: normalFontSize)
messageLabel.textAlignment = .center
messageLabel.sizeToFit()
messageLabel.translatesAutoresizingMaskIntoConstraints = false
let errorImage: UIImageView = {
let errorImage = UIImageView()
errorImage.translatesAutoresizingMaskIntoConstraints = false
return errorImage
}()
self.backgroundView = emptyView
emptyView.addSubview(contentView)
contentView.addSubview(errorImage)
contentView.addSubview(messageLabel)
contentView.centerYAnchor.constraint(equalTo: emptyView.centerYAnchor).isActive = true
contentView.centerXAnchor.constraint(equalTo: emptyView.centerXAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo: emptyView.leadingAnchor, constant: normalSpacing * 3).isActive = true
contentView.trailingAnchor.constraint(equalTo: emptyView.trailingAnchor, constant: -(normalSpacing * 3)).isActive = true
contentView.topAnchor.constraint(equalTo: errorImage.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor).isActive = true
messageLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
messageLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
messageLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
func restore() {
self.backgroundView = nil
}
}
And I set it like this:
if(tableData.isEmpty) {
self.tableView.setEmptyMessage("No results!", "none")
} else {
self.tableView.restore()
}
self.tableView.reloadData()
No big deal, we all have seen this and probably used it. And it works great. However, I now have a UIButton on the bottom placed in the tableFooterView. This button stays on top of the UITableView because it automatically positions itself right under the last cell, which is precisely what I want when there is data, but now the empty message is shown in the middle of the screen while the button is above it. How can I fix this so there is a sort of frame when the dataSource is empty?
To illustrate:
So now you are setting your tableView backgroundView to your emptyView. You can take a look on your dataSource and if it's empty - just return new cell for empty state with this message.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count > 0 ? dataSource.count : 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if dataSource.count.isEmpty 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "EmptyCell")
cell.textLabel.text = "No Results!"
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "SomeCell")
return cell
}
}
Write an extension
extension UITableView
{
func addErrorMessageLabel(noDataText:String = "No data available")->UILabel
{
let noDatalabel:UILabel!
noDatalabel=UILabel(frame: CGRect(x: self.frame.size.width/2-200, y: self.frame.size.height/2-50, width: 400, height: 100))
noDatalabel.textColor = textThemeColor
noDatalabel.text=noDataText
noDatalabel.numberOfLines=0
noDatalabel.textAlignment = .center
noDatalabel.isHidden=true
self.addSubview(noDatalabel)
self.alignCenterToSuperView(item: noDatalabel, horizentally: true, vertically: true,height: 100,width: 400)
return noDatalabel
}
func alignCenterToSuperView(item:UIView,horizentally:Bool,vertically:Bool , height:Int, width:Int)
{
if horizentally
{
item.translatesAutoresizingMaskIntoConstraints = false
let xConstraint = NSLayoutConstraint(item: item, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0)
NSLayoutConstraint.activate([xConstraint])
}
if vertically
{
item.translatesAutoresizingMaskIntoConstraints = false
let yConstraint = NSLayoutConstraint(item: item, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0)
NSLayoutConstraint.activate([yConstraint])
}
let Height = NSLayoutConstraint(item: item, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant:CGFloat(height))
item.addConstraint(Height)
let Width = NSLayoutConstraint(item: item, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant:CGFloat(width))
item.addConstraints([Width,Height])
}
}
In your ViewController create
var noDataLabel:UILabel!
In your ViewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
noDataLabel = tableView.addErrorMessageLabel()
noDataLabel.text = "No data found" // or your message
}
And final step
if(tableData.isEmpty) {
noDataLabel.isHideen = false
} else {
noDataLabel.isHideen = true
}

Swift - Dynamic number of buttons in tableviewcell

Actually i have a project in github. The problem is that i cannot get why when scrolling, the buttons constraints in the cells are going crazy..
I didn't saw any project like this, then i have a reason to share it but i want to give a good example for another people.
I'll be very thankful with any help that drives me to the solution of this problem.
Best regards.
There it is the code for the cell:
import UIKit
class BookTableViewCell: UITableViewCell {
let nameLabel = UILabel()
let detailLabel = UILabel()
var cellButton = UIButton()
var cellLabel = UILabel()
var book : Book!
// MARK: Initalizers
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
func configureCellCon(botones:Int, titulo:String, book:Book) {
self.book = book
let marginGuide = contentView.layoutMarginsGuide
// configure titleLabel
contentView.addSubview(nameLabel)
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
nameLabel.topAnchor.constraint(equalTo: marginGuide.topAnchor).isActive = true
nameLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
nameLabel.numberOfLines = 0
nameLabel.font = UIFont(name: "AvenirNext-DemiBold", size: 16)
nameLabel.text = book.name
// configure authorLabel
contentView.addSubview(detailLabel)
detailLabel.translatesAutoresizingMaskIntoConstraints = false
detailLabel.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
// detailLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
detailLabel.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
detailLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor).isActive = true
detailLabel.numberOfLines = 0
detailLabel.font = UIFont(name: "Avenir-Book", size: 12)
detailLabel.textColor = UIColor.lightGray
detailLabel.text = book.details
var lastButton = UIButton()
if(book.buttonsAttibutes.count == 1) {
let button = UIButton()
button.tag = 0
button.backgroundColor = UIColor.init(white: 0.9, alpha: 0.0)
// button.setBackgroundColor(color: UIColor.white, forState: .normal)
// button.setBackgroundColor(color: UIColor.blue, forState: .normal)
button.setTitle(book.buttonsAttibutes[0].title, for: .normal)
button.setTitleColor(UIColor.blue.withAlphaComponent(0.7), for: .normal)
button.setTitleColor(UIColor.init(white: 0.8, alpha: 1), for: .highlighted)
button.layer.borderWidth = 0
button.layer.borderColor = UIColor.blue.cgColor
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
contentView.addConstraints([
NSLayoutConstraint(item: button, attribute: .leftMargin, relatedBy: .equal, toItem: contentView, attribute: .leftMargin, multiplier: 1.0, constant: 20),
NSLayoutConstraint(item: button, attribute: .rightMargin, relatedBy: .equal, toItem: contentView, attribute: .rightMargin, multiplier: 1.0, constant: -20),
])
button.topAnchor.constraint(equalTo: detailLabel.bottomAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
button.addTarget(self, action:#selector(mostrarMensaje), for:.touchUpInside)
return
}else if(book.buttonsAttibutes.count == 0) {
detailLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
return
}
if(book.buttonsAttibutes.count > 0) {
for x in 0...(book.buttonsAttibutes.count - 1) {
let button = UIButton()
button.tag = x
button.backgroundColor = UIColor.init(white: 0.9, alpha: 0.0)
// button.setBackgroundColor(color: UIColor.white, forState: .normal)
// button.setBackgroundColor(color: UIColor.blue, forState: .normal)
button.setTitle(book.buttonsAttibutes[x].title, for: .normal)
button.setTitleColor(UIColor.blue.withAlphaComponent(0.7), for: .normal)
button.setTitleColor(UIColor.init(white: 0.8, alpha: 1), for: .highlighted)
button.layer.borderWidth = 0
button.layer.borderColor = UIColor.blue.cgColor
contentView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
// button.leadingAnchor.constraint(equalTo: marginGuide.leadingAnchor).isActive = true
// button.trailingAnchor.constraint(equalTo: marginGuide.trailingAnchor).isActive = true
contentView.addConstraints([
NSLayoutConstraint(item: button, attribute: .leftMargin, relatedBy: .equal, toItem: contentView, attribute: .leftMargin, multiplier: 1.0, constant: 20),
NSLayoutConstraint(item: button, attribute: .rightMargin, relatedBy: .equal, toItem: contentView, attribute: .rightMargin, multiplier: 1.0, constant: -20),
])
if(x == 0){
if #available(iOS 11.0, *) {
button.layer.cornerRadius = 8
button.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
} else {
button.layer.cornerRadius = 5
}
button.topAnchor.constraint(equalTo: detailLabel.bottomAnchor).isActive = true
}else if(x == (book.buttonsAttibutes.count - 1)){
if #available(iOS 11.0, *) {
button.layer.cornerRadius = 8
button.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
} else {
button.layer.cornerRadius = 5
}
button.topAnchor.constraint(equalTo: lastButton.bottomAnchor).isActive = true
button.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor).isActive = true
}else{
button.topAnchor.constraint(equalTo: lastButton.bottomAnchor).isActive = true
}
button.addTarget(self, action:#selector(mostrarMensaje), for:.touchUpInside)
lastButton = button
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func mostrarMensaje(sender: UIButton){
let message = self.book.buttonsAttibutes[sender.tag].message
let alertController = UIAlertController(title: "\(self.book.name)", message: message, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Cerrar", style: UIAlertActionStyle.default) {
(result : UIAlertAction) -> Void in
}
alertController.addAction(okAction)
if let myViewController = parentViewController {
print(myViewController.title ?? "ViewController without title.")
myViewController.present(alertController, animated: true, completion: nil)
}
}
}
Ok, i went for the "InterfaceBuilder" and did it properly.
However, I'll be reading more about programmatic methods to do it.
thanks to all of you for the guide.
When creating the buttons and constraints programmatically, they are generating more constraints over the same cells making them crash.

Custom View size in iOS not dynamic

I just have started my first app on iOS a week ago. I have created a custom view to use it in my app using AppleDeveloperWebsite Custom Rating Control Tutorial.
Now I have chosen iPhone7 device in storyboard and I run this on iPhone 7 emulator it works perfectly but when I run it on iPhone 5 emulator (size of screen changes) my custom views extend beyond the screen. My all other controls sizes resize as I set constraints but only my custom view sizes get messed up.
Please Help
import UIKit
#IBDesignable class xRadioButtonView: UIView {
var button: UIButton!
var label: UILabel!
#IBInspectable var text: String? {
didSet{
label.text = text
}
}
//Properties
var isSelected = 0
//Initialization
override init(frame: CGRect){
super.init(frame: frame)
addSubviews()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
addSubviews()
}
func addSubviews() {
self.backgroundColor = UIColor.white
let xWidth = bounds.size.width
let xHeight = bounds.size.height
let tap = UITapGestureRecognizer(target: self, action: #selector(xRadioButtonView.radioButtonTextTapped))
button = UIButton(frame: CGRect(x: 1, y: 1, width: xWidth - 2, height: xHeight - 4))
button.backgroundColor = UIColor.white
button.addTarget(self, action: #selector(xRadioButtonView.radioButtonTapped(button:)), for: .touchDown)
addSubview(button)
label = UILabel(frame: CGRect(x: 1, y: 1, width: xWidth - 2, height: xHeight - 2))
label.textColor = UIColor.init(hex: "#D5D5D5")
//label.font = UIFont.init(name: label.font.fontName, size: 25)
//label.font = label.font.withSize(25)
label.font = UIFont.boldSystemFont(ofSize: 25)
label.textAlignment = NSTextAlignment.center
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tap)
addSubview(label)
}
override func layoutSubviews() {
// Set the button's width and height to a square the size of the frame's height.
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
label.text = "xRBV"
}
func radioButtonTapped(button: UIButton) {
if isSelected == 0 {
isSelected = 1
self.backgroundColor = UIColor.init(hex: "#00BFA5")
label.textColor = UIColor.init(hex: "#00BFA5")
} else {
isSelected = 0
self.backgroundColor = UIColor.white
label.textColor = UIColor.init(hex: "#D5D5D5")
}
}
func radioButtonTextTapped(sender: UITapGestureRecognizer){
if isSelected == 0 {
isSelected = 1
self.backgroundColor = UIColor.init(hex: "#00BFA5")
label.textColor = UIColor.init(hex: "#00BFA5")
} else {
isSelected = 0
self.backgroundColor = UIColor.white
label.textColor = UIColor.init(hex: "#D5D5D5")
}
}
}
As you can see PG button should finish where green color finishes but white color button is extended beyond the screen
You either need to set the frame in layoutSubViews or you need to implement autolayout in code:
button = UIButton(frame: CGRect(x: 1, y: 1, width: xWidth - 2, height: xHeight - 4))
button.backgroundColor = UIColor.white
button.addTarget(self, action: #selector(xRadioButtonView.radioButtonTapped(button:)), for: .touchDown)
addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
let attributes: [NSLayoutAttribute] = [.top, .bottom, .leading, .trailing]
let constants = [2, 2, 10, 10] // 2 from top and bottom, 10 from leading and trailing
NSLayoutConstraint.activate(attributes.enumerated().map { NSLayoutConstraint(item: button, attribute: $1, relatedBy: .equal, toItem: button.superview, attribute: $1, multiplier: 1, constant: constants[$0]) })
The example uses the old way because your constraints are uniform, but if you have something more complicated its often simpler to use NSLayoutAnchor as of iOS 9.
EDIT: here is the code for tuples if anyone is interested:
button.translatesAutoresizingMaskIntoConstraints = false
let attributes: [(NSLayoutAttribute, CGFloat)] = [(.top, 2), (.bottom, 2), (.leading, 12), (.trailing, 12)]
NSLayoutConstraint.activate(attributes.map { NSLayoutConstraint(item: button, attribute: $0.0, relatedBy: .equal, toItem: button.superview, attribute: $0.0, multiplier: 1, constant: $0.1) })
Thanks I wasn't even aware of this NSLayout yet. (HEHE 7 Days) Thanks to you I have a solution for my problem. Although I wanted different values for .top .bottom .leading .trailling
I used your code like this
NSLayoutConstraint(item: button, attribute: .top, relatedBy: .equal, toItem: button.superview, attribute: .top, multiplier: 1, constant: 1).isActive = true
NSLayoutConstraint(item: button, attribute: .bottom, relatedBy: .equal, toItem: button.superview, attribute: .bottom, multiplier: 1, constant: 4).isActive = true
to all 4 sides. But is there a way to provide constant values as well like you have provided multiple attribute values?

UITableView damping animation and layout constraints

I'm trying to animate UITableView to act like a dropdownMenu by using its height constraint and UIView.animateWithDamping(..) block. I'm occuring weird problem with white background under tableView.
iPhone Simulator showing the problem
I have cleared each background color and it doesn't help much.
Here is the code setting all subviews of dropDownView, which is a UIView:
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.elements = []
defaultSetup()
}
private func defaultSetup() {
configureActionButton()
configureTableView()
}
private func configureActionButton() {
actionButton = UIButton(frame: CGRectZero)
actionButton.translatesAutoresizingMaskIntoConstraints = false
addSubview(actionButton)
guard let superview = actionButton.superview else {
assert(false, "ActionButton adding to superview failed.")
return
}
// Constraints
actionButton.constrain(.Leading, .Equal, superview, .Leading, constant: 0, multiplier: 1)?.constrain(.Trailing, .Equal, superview, .Trailing, constant: 0, multiplier: 1)?.constrain(.Top, .Equal, superview, .Top, constant: 0, multiplier: 1)?.constrain(.Bottom, .Equal, superview, .Bottom, constant: 0, multiplier: 1)
// Appearance
actionButton.backgroundColor = UIColor.clearColor()
actionButton.opaque = false
actionButton.contentHorizontalAlignment = .Left
actionButton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 0)
if borderVisible {
actionButton.layer.cornerRadius = 5
actionButton.layer.borderColor = UIColor.blackColor().CGColor
actionButton.layer.borderWidth = 1
actionButton.clipsToBounds = true
}
// Actions
actionButton.addTarget(self, action: "menuAction:", forControlEvents: .TouchUpInside)
}
private func configureTableView() {
tableView = BOTableView(frame: CGRectZero, items: elements, configuration: configuration)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.delegate = self
tableView.dataSource = self
addSubview(tableView)
guard let tableViewSuperview = tableView.superview else {
assert(false, "TableView adding to superview failed.")
return
}
// Constraints
tableView.constrain(.Trailing, .Equal, tableViewSuperview, .Trailing, constant: 0, multiplier: 1)?.constrain(.Top, .Equal, tableViewSuperview, .Bottom, constant: 0, multiplier: 1)?.constrain(.Leading, .Equal, tableViewSuperview, .Leading, constant: 0, multiplier: 1)
tvHeightConstraint = NSLayoutConstraint(item: tableView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 0)
tableView.addConstraint(tvHeightConstraint)
}
BOTableView class initializer:
init(frame: CGRect, items: [String], configuration: BOConfiguration) {
super.init(frame: frame, style: UITableViewStyle.Plain)
self.items = items
self.selectedIndexPath = NSIndexPath(forRow: 0, inSection: 0)
self.configuration = configuration
// Setup table view
self.opaque = false
self.backgroundView?.backgroundColor = UIColor.clearColor()
self.backgroundColor = UIColor.clearColor()
self.separatorColor = UIColor.blackColor()
self.scrollEnabled = false
self.separatorStyle = .SingleLine
self.layer.cornerRadius = 5
self.layer.borderColor = UIColor.blackColor().CGColor
self.layer.borderWidth = 1
self.clipsToBounds = true
}
UIView animations:
private func showMenuWithCompletionBlock(completion: (succeeded: Bool) -> Void) {
delegate?.menuWillShow(self)
let tvHeight = frame.size.height * CGFloat(elements.count)
tvHeightConstraint.constant = tvHeight
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.5, options: .CurveEaseInOut, animations: { [weak self] () -> Void in
guard let strongSelf = self else {
completion(succeeded: false)
return
}
strongSelf.layoutIfNeeded()
}, completion: { (finished) -> Void in
if finished {
completion(succeeded: true)
}
})
}
Here is the code for UIView + Constraints extension, used in code:
extension UIView {
/**
:returns: true if v is in this view's super view chain
*/
public func isSuper(v : UIView) -> Bool
{
for var s : UIView? = self; s != nil; s = s?.superview {
if(v == s) {
return true;
}
}
return false
}
public func constrain(attribute: NSLayoutAttribute, _ relation: NSLayoutRelation, _ otherView: UIView, _ otherAttribute: NSLayoutAttribute, constant: CGFloat = 0.0, multiplier : CGFloat = 1.0) -> UIView?
{
let c = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: relation, toItem: otherView, attribute: otherAttribute, multiplier: multiplier, constant: constant)
if isSuper(otherView) {
otherView.addConstraint(c)
return self
}
else if(otherView.isSuper(self) || otherView == self)
{
self.addConstraint(c)
return self
}
assert(false)
return nil
}
public func constrain(attribute: NSLayoutAttribute, _ relation: NSLayoutRelation, constant: CGFloat, multiplier : CGFloat = 1.0) -> UIView?
{
let c = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: relation, toItem: nil, attribute: .NotAnAttribute, multiplier: multiplier, constant: constant)
self.addConstraint(c)
return self
}
}
When I tried to debug the views' hierarchy in debugger, the only view which had white background was tableView, but I have cleared the background in code. I have also tried to set tableView's backgroundView to nil as well as backgroundView.backgroundColor to clearColor(). Nothing changed.
Maybe try to set the UITableView footer to a blank view, don't really know why, but it seams to help for similar issue like You have.
[_tableView setTableFooterView:[[UIView alloc] init]];

Swift : How to make a Black screen to guidelines for user to use app

Im using swift 1.2.
I need to create a guideline for user to use my app.
it look like: a black transparent screen, 1 button highlight with note how to use, what is it.
i have searching for solution but still get stuck.
Can anybody help me?
Thanks!
If I understood correctly what you want, it can be done something like this*:
class TutorialView : UIView
{
override init(frame: CGRect)
{
super.init(frame: frame)
self.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5)
}
convenience init()
{
self.init(frame: CGRectZero)
}
required init?(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
func showFromWindow()
{
let window = UIApplication.sharedApplication().keyWindow!
self.translatesAutoresizingMaskIntoConstraints = false
window.addSubview(self)
window.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[tutorialView]-0-|", options: NSLayoutFormatOptions.DirectionLeftToRight, metrics: nil, views: ["tutorialView": self]))
window.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[tutorialView]-0-|", options: NSLayoutFormatOptions.DirectionLeftToRight, metrics: nil, views: ["tutorialView": self]))
}
func addCommentToTopRightItem(comment: String, afterDelay: NSTimeInterval)
{
let topMargin : Int32 = 54
let rightMargin : Int32 = 24
let height : Int32 = 100
// we need two views - one for arrow and second for text
let commentLabel = UILabel()
let arrowView = UIView()
commentLabel.translatesAutoresizingMaskIntoConstraints = false
arrowView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(commentLabel)
self.addSubview(arrowView)
// setup layout for views
// |^^^^^|
// |^^^^^|arrow|
// |label|_____|
// |_____|
//
// we place center of Y label to the bottom of arrow view
// to an arrow coming out of the middle of text
let metrics = ["topmargin" : NSNumber(int: topMargin), "rightmargin": NSNumber(int: rightMargin),"height" : NSNumber(int: height)]
let views = ["label": commentLabel, "arrow": arrowView]
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-topmargin-[arrow(==height)]", options: NSLayoutFormatOptions.DirectionLeftToRight, metrics: metrics, views: views))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[label(==height)]", options: NSLayoutFormatOptions.DirectionLeftToRight, metrics: metrics, views: views))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-rightmargin-[arrow(==height)]-[label]-0-|", options: NSLayoutFormatOptions.DirectionRightToLeft, metrics: metrics, views: views))
self.addConstraint(NSLayoutConstraint(item: commentLabel, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: arrowView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 1.0))
// configure text label
commentLabel.textColor = UIColor.whiteColor()
commentLabel.numberOfLines = 0
commentLabel.textAlignment = NSTextAlignment.Right
commentLabel.font = UIFont(name: "HelveticaNeue-Light", size: 16)
commentLabel.text = comment
// configure arrow view
self.layoutIfNeeded()
let d : CGFloat = min(arrowView.frame.size.height, arrowView.frame.size.width)
// draw arc line from {0, 1} to {1, 0}
// | /|
// |/_|
let linePath = UIBezierPath()
linePath.moveToPoint(CGPointMake(0, d))
linePath.addCurveToPoint(CGPointMake(d, 0), controlPoint1: CGPointMake(d*0.5, d), controlPoint2: CGPointMake(d, d*0.5))
let lineLayer = CAShapeLayer()
lineLayer.path = linePath.CGPath
lineLayer.strokeColor = UIColor.redColor().CGColor
lineLayer.backgroundColor = UIColor.clearColor().CGColor
lineLayer.fillColor = UIColor.clearColor().CGColor
lineLayer.lineWidth = 2
// draw triangle near {1, 0} point
let trianglePath = UIBezierPath()
trianglePath.moveToPoint(CGPointMake(d, 0))
trianglePath.addLineToPoint(CGPointMake(d - 5, 15))
trianglePath.addLineToPoint(CGPointMake(d + 5, 15))
trianglePath.closePath()
let triangleLayer = CAShapeLayer()
triangleLayer.path = trianglePath.CGPath
triangleLayer.strokeColor = UIColor.redColor().CGColor
triangleLayer.backgroundColor = UIColor.clearColor().CGColor
triangleLayer.fillColor = UIColor.redColor().CGColor
triangleLayer.lineWidth = 2
// line + triangle = arrow :)
arrowView.layer.addSublayer(lineLayer)
arrowView.layer.addSublayer(triangleLayer)
arrowView.alpha = 0.0
commentLabel.alpha = 0.0
UIView.animateWithDuration(0.8, delay: afterDelay, options: UIViewAnimationOptions.AllowAnimatedContent, animations:
{ () -> Void in
arrowView.alpha = 1.0
commentLabel.alpha = 1.0
}, completion: nil)
}
func addButton(title: String, highlighteAfterDelay: NSTimeInterval)
{
// same as before
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(button)
self.addConstraint(NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 1.0))
self.addConstraint(NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: -20.0))
self.addConstraint(NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 120.0))
self.addConstraint(NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 40.0))
button.setTitle(title, forState: UIControlState.Normal)
button.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
button.layer.cornerRadius = 20.0
button.layer.masksToBounds = false
button.layer.borderWidth = 1.0
button.layer.borderColor = UIColor.whiteColor().CGColor
button.layer.shadowColor = UIColor.whiteColor().CGColor
button.layer.shadowOpacity = 0.4
button.layer.shadowRadius = 4
button.layer.shadowOffset = CGSizeMake(0, 0)
button.alpha = 0.2
button.enabled = false
UIView.animateWithDuration(0.8, delay: highlighteAfterDelay, options: UIViewAnimationOptions.AllowAnimatedContent, animations:
{ () -> Void in
button.alpha = 1.0
button.enabled = true
}, completion: nil)
}
}
In some view controller:
let tutorial = TutorialView()
tutorial.showFromWindow()
tutorial.addCommentToTopRightItem("Tap this button to do something amazing action!", afterDelay: 1.0)
tutorial.addButton("Done", highlighteAfterDelay: 3.0)
Result:
*I don't have a compiler version 1.2 but I think that there should be no serious differences.
I see the problem now, you can add UView on top of it, give it a color with alpha, and get frame by using
convertRect(button.frame, toView:overlayView)
Hopefuly helps

Resources