when UITableViewCell layout and constraints get final? - ios

I have a label in my TableViewCell and I will set it's text throw a variable in cell (for example myText), like this:
class ProjectManagementTableViewCell: UITableViewCell {
#IBOutlet weak var myLabel: UILabel!
var myText: String? {
didSet {
if let _myText = self.myText {
self.myLabel.text = _myText
} else {
self.myLabel.text = ""
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
I didn't fixed myLabel width size in InterfaceBuilder, so I expect it get resize according to text it gets from cellForRowAt function. everything good till now. but I want label width to be less than some value, and beside this, I have an image that I want to be 10 pt distance from end of text of label. so I need to have access to label width or position of UIElements after they get repositioned in response to assigning text to label. I tried several locations to find when I can access new values for frame positions, but I failed, like awakeFromNib and didSet of myText. so when I can access to final values of frames and constraints in UITableViewCell?

Actually you don't need
var myText: String? {
didSet {
if let _myText = self.myText {
self.myLabel.text = _myText
} else {
self.myLabel.text = ""
}
}
}
just assign the text inside
let cell = //
cell.lbl.text = "text_here"
or create a configure function inside the custom cell class and call it
the label will resize according to the the current constraints
also final frames inside
override func layoutSubviews()
super.layoutSubviews()
}

Related

Getting properties of a UIButton in a UITableViewCell

I am entirely new at Swift, and am building a referral form. Some pages of the form have tickboxes where the user can select what type of support they need. I've created these tickboxes as cells for a UITableView. They have their own view and nib, and are made up of a button and an image for feedback. Here's the code:
class TickboxCell: UITableViewCell {
#IBOutlet weak var supportBox: UIView!
#IBOutlet weak var supportBtn: UIButton!
#IBOutlet weak var checkmark: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func buttonPressed(_ sender: UIButton) {
if supportBtn.isSelected == false {
supportBtn.isSelected = true
checkmark.image = UIImage(named: K.Images.checked)
} else {
supportBtn.isSelected = false
checkmark.image = UIImage(named: K.Images.notChecked)
}
}
}
In the ViewController for the page, I'm trying to create a function whereby, when the user presses the Next button to go to the next page of the form, all the labels of the buttons that have been selected are stored and then passed onto the final page of the referral form, ready to be emailed.
This is where I'm at with the function:
func getTicks() {
var ticks: [String:String?] = [:]
for cell in self.tableView.visibleCells {
if cell.supportBtn.isSelected == true {
if let title = cell.supportBtn.titleLabel?.text {
ticks.updateValue("Yes", forKey: title)
}
}
}
}
Of course, this doesn't work because the visible cells don't have what I'm looking for. They only have properties like baseClass and frame.
In my function for the tableView, there's the line
let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath) as! TickboxCell
I know that's where it makes the tableView cells into the Tickbox cells - I set the titleLabels for the cell buttons after that. So can I work this into the above function somehow? Or is there another way to tap into the selected status and titles of my buttons?
This is my first ever question on here, apologies if there's anything wrong with it!

xcode: label not showing after adding constraint

I have a collectionViewCell, I added to it one label, I set constraint spacing to nearest neighbor to (0,0,0,0), so the must take all cell and adjust to its height and width, but the label is not showing at all.
I realized that what ever I put in a collectionCell, if I added to it a constraint, It well not be showing, so I ended up deleting all constraints and manually set label's height and width
CollectionViewCell:
import UIKit
class CollectionViewCell: UICollectionViewCell {
var text:String?
var delegate: TableViewCell?
#IBOutlet weak var label: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
loadFromNib() // load xib
let tap = UITapGestureRecognizer(target: self, action: #selector(tapFunc))
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tap)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapFunc(_ sender: Any) {
// head will not be clickable cuz parent here gonna be nil
if let p = delegate {
if p.isUserInteractionEnabledWith(cell: self){
p.didTapeLabel(for: self, value: label.text!)
}
} else {
print("parent at collectionview cell is nil")
}
}
func fillOutData(_ text:String) {
label.text = text
}
}
enter image description here
I am building a multicolumn tableView :
tableView --> tableViewCell --> CollectionView --> CollectionViewCell --> label
Set data source and delegate for UICollectionView inside UITableViewCell.
override func awakeFromNib() {
//collection View Delegates
self.collectionView.delegate = self
self.collectionView.dataSource = self
super.awakeFromNib()
}

Change UITableViewCell height based on webview content

I have a UIWebView nested within a UITableViewCell.
How can I adjust the cell's height depending on the content I load into the WebView (i.e. after it finishes downloading) ... ?
I have the tableview to set the heightForRowAtPath to be UITableViewAutomaticDimension.
loadContentIntoWebview is called by the owner of the TableView in it's cellForRowAt delegate methods.
I'm noticing heightForRowAt being called before cellForRowAt so my "solution" of setting the height based on the cell's webview's content size won't work.
import UIKit
class MyCell: UITableViewCell, UIWebViewDelegate {
// MARK: - instance variables
#IBOutlet weak var myWebview: UIWebView?
// MARK: - lifecycle methods
override func awakeFromNib() {
super.awakeFromNib()
myWebview?.scrollView.bounces = false;
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
// MARK: - private methods
func loadContentIntoWebview(htmlString: String) {
myWebview?.loadHTMLString(htmlString, baseURL: nil)
}
// MARK: - UIWebViewDelegate methods
func webViewDidFinishLoad(_ webView: UIWebView) {
// dynamically set WebView height
let tempString = myWebview?.stringByEvaluatingJavaScript(from: "document.body.scrollHeight")
var tempHeight: CGFloat = 0.0
if let n = NumberFormatter().number(from: tempString!) {
tempHeight = CGFloat(n)
}
myWebview?.frame.size.height = tempHeight
}
}
func webViewDidFinishLoad(_ webView: UIWebView) {
if let cell :yourCell = self.yourTblVw.cellForRow(at: yourindexpath) as? yourCell{
// set height of webview as per its content
var frame = cell.webView.frame
frame.size.height = 1
let fittingSize = cell.webView.sizeThatFits(CGSize.zero)
frame.size = fittingSize
cell.heightWebVwMain.constant = fittingSize.height
}
where heighWebVwMain is constraint of your cell's webview's height. So you have to set this constant in cellforRow method also
Found a solution ...
In webViewDidFinishLoad(_ webView: UIWebView) set the constraint's
height as in initial question but use document.body.clientHeight
Blast a notification to tell the associated tableview to not reloadData(), but rather beginUpdates() then endUpdates()
#objc func handleWebviewFinishedLoading() {
self.webview?.beginUpdates()
self.webview?.endUpdates()
}
This seems to redraw the cells without reloading them :)

(Xcode 8 Swift 3). Using Custom Keyboard Extension with a particular Text Field

Since standard Number Pad keyboard has "empty" button, but doesn't have "+/-" button, I decided to create my own Keyboard Extension. I've done it.
But I don't know how to link (and invoke) it with a particular Text Field while other Text Fields using usual keyboards.
Is there any opportunity to apply custom keyboardType like my own custom Keyboard?
I found solution based on simular question: How to input text using the buttons of an in-app custom keyboard
import UIKit
protocol KeyboardDelegate: class {
func keyWasTapped(text: String)
}
class KeyboardView: UIView {
// This variable will be set as the view controller so that
// the keyboard can send messages to the view controller.
weak var delegate: KeyboardDelegate?
// MARK:- keyboard initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
initializeSubviews()
}
func initializeSubviews() {
let xibFileName = "KeyboardView" // xib extention not included
let view = Bundle.main.loadNibNamed(xibFileName, owner: self, options: nil)?[0] as! UIView
self.addSubview(view)
view.frame = self.bounds
}
// MARK:- Button actions from .xib file
#IBAction func keyTapped(_ sender: UIButton) {
// When a button is tapped, send that information to the
// delegate (ie, the view controller)
self.delegate?.keyWasTapped(text: sender.titleLabel!.text!) // could alternatively send a tag value
}
}
/* when error: "Could not load NIB in bundle"
Could not load NIB in bundle
Visit the properties of the .xib files in the file inspector ,the property "Target Membership" pitch on the select box ,then your xib file was linked with your target
*/
In main ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate, KeyboardDelegate {
#IBOutlet weak var text1: UITextField!
#IBOutlet weak var text2: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// initialize custom keyboard
let keyboardView = KeyboardView(frame: CGRect(x: 0, y: 0, width: 375, height: 165))
keyboardView.delegate = self // the view controller will be notified by the keyboard whenever a key is tapped
// replace system keyboard with custom keyboard
text1.inputView = keyboardView //accessoryView
text1.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// required method for keyboard delegate protocol
func keyWasTapped(text character: String) {
if Int(character) != nil{
text1.insertText(character)
}
if character == "⌫" {
if !(text1.text?.isEmpty)! {
let beforeText = text1.text!
let truncated = beforeText.substring(to: beforeText.index(before: beforeText.endIndex))
text1.text = truncated
}
}
if character == "±" {
let beforeText = text1.text!
if var number = Int(beforeText) {
number = -number
text1.text = "\(number)"
}
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
/*
if (textField == self.text1) {
//textField.inputView = keyboardView
}
*/
}
}
I have coded a calculator for metric/imperial system. For that, I have coded my own keyboard, too.
I have set up the keyboard as UIButtons stacked within a Stack View.
The Buttons seem to be unknown for Xcode since I have just upgraded to Xcode 8 and the project is still in Swift 2.2.
Then I have set a UITextField and filled its text property using my buttons. This is for example the function for the Button 1 on my keyboard.
#IBOutlet weak var inputField: UITextField!
var numberStr:String = "0"
inputField.text = numberStr
#IBAction func oneKeyboardAction(sender: AnyObject) {
if numberStr == "0" {
numberStr = String(numberStr.characters.dropLast())
}
let newStr:String = numberStr + String("1")
numberStr = newStr
let dotToCommaString = newStr.stringByReplacingOccurrencesOfString(".", withString: ",")
inputField.text = dotToCommaString
}
Also I have deactivated user interaction with the TextField, so the "original Keyboard" will not show.
Edit
Like mentioned in the comment section and to have my answer better fit your needs. You could set my custom keyboard into a UIView overlapping your UIViewController inside the Interface Builder. Set it as MyKeyboardView.hidden = true inside the viewDidLoad().
Then you have your TextField where you want the custom Keyboard to be visible instead of the system one:
func textFieldDidBeginEditing(_ textField: UITextField) {
if (textField == self.yourDesiredTextField) { //the one where you want to use the custom keyboard
MyKeyboardView.hidden = false
}
}
Then you add a gesture recognizer like that:
override func viewDidLoad() {
super.viewDidLoad()
MyKeyboardView.hidden = true
let tap = UITapGestureRecognizer(target: self, action: #selector(gesture))
tap.numberOfTapsRequired = 1
view.addGestureRecognizer(tap)
}
func gesture() {
UIView.animateWithDuration(0.2) {
self.MyKeyboardView.hidden = true
}
}
To have it little more smooth, I have added animateWithDuration when hiding the keyboard.

Picker View inside a custom Table View cell returns runtime error

I am trying to implement a horizontal picker view (AKPickerView-Swift from GitHub) inside a custom table view cell.
The picker view is a custom class, and in the prototype cell view in the storyboard, I added two UIViews that are of that class, and wired them as IBOutlet to my custom UITableViewCell class.
When I run the program, it crashes with the error:
EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
at the line in the custom UITableViewCell class that says self.eventTypePicker.delegate = self (see image).
On the right hand side of that screenshot, you can see an implementation of the same picker view in a regular view controller, which works fine.
The output window at the bottom of the screen in XCode shows the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
What should I do differently when I am trying to implement these picker views in a table view cell instead of a regular view controller?
I am using XCode 7.1 and writing in Swift.
Here's the code in my WorkoutTableViewCell.swift class
import UIKit
class WorkoutTableViewCell: UITableViewCell, AKPickerViewDataSource, AKPickerViewDelegate {
#IBOutlet weak var eventSelectorLabel: UILabel!
#IBOutlet weak var repsSelectorLabel: UILabel!
#IBOutlet weak var eventTypePicker: AKPickerView!
#IBOutlet weak var repsPicker: AKPickerView!
let distance = ["100m", "100m h", "200m", "200m h", "400m", "400m h"]
let sets = ["-", "1", "2", "3", "4", "5", "6", "7"]
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
self.eventTypePicker.delegate = self
self.eventTypePicker.dataSource = self
self.eventTypePicker.font = UIFont(name: "HelveticaNeue-Light", size: 10)!
self.eventTypePicker.highlightedFont = UIFont(name: "HelveticaNeue", size: 10)!
self.eventTypePicker.pickerViewStyle = .Wheel
self.eventTypePicker.maskDisabled = false
self.eventTypePicker.reloadData()
self.repsPicker.delegate = self
self.repsPicker.dataSource = self
self.repsPicker.font = UIFont(name: "HelveticaNeue-Light", size: 10)!
self.repsPicker.highlightedFont = UIFont(name: "HelveticaNeue", size: 10)!
self.repsPicker.pickerViewStyle = .Wheel
self.repsPicker.maskDisabled = false
self.repsPicker.reloadData()
eventTypePicker.tag = 0
repsPicker.tag = 1
}
// MARK: - AKPickerViewDataSource
func numberOfItemsInPickerView(effortPicker: AKPickerView) -> Int {
if effortPicker.tag == 0{
return self.sets.count
} else if effortPicker.tag == 1 {
return self.distance.count
}
return 1
}
func pickerView(effortPicker: AKPickerView, titleForItem item: Int) -> String {
if effortPicker.tag == 0{
return self.sets[item]
} else if effortPicker.tag == 1 {
return self.distance[item]
}
return ""
}
func effortPicker(effortPicker: AKPickerView, imageForItem item: Int) -> UIImage {
return UIImage(named: self.distance[item])!
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// println("\(scrollView.contentOffset.x)")
}
}

Resources