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 :)
Related
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()
}
I would like to play a sound file when a new page comes on the screen.
So far I did that to have the pageControler working with an array of images.
Can someone tell me where I have to put my code in order to play an array of sound? So that the sound1 will be played when the image1 comes on the screen.
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
var images: [String] = ["1", "2", "3", "4", "5"]
var frame = CGRect(x:0,y:0, width:0, height:0)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
pageControl.numberOfPages = images.count
for index in 0..<images.count {
frame.origin.x = scrollView.frame.size.width * CGFloat(index)
frame.size = scrollView.frame.size
let imgView = UIImageView(frame: frame)
imgView.image = UIImage(named: images[index])
self.scrollView.addSubview(imgView)
}
scrollView.contentSize = CGSize(width:(scrollView.frame.size.width * CGFloat(images.count)), height: scrollView.frame.size.height)
scrollView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
pageControl.currentPage = Int(pageNumber)
}
}
I don't see a direct way to get called when the selected page changes. You could add code in your page view controller's setViewControllers(_:direction:animated:completion:) method that figures out which page is active and plays the appropriate sound.
If you use a page control you could probably also subclass UIPageControl and use a didSet on the currentPage property to figure out which sound to play.
Edit:
Just add a new file, make it a Cocoa touch class, and make it a subclass of UIPageControl. Name it CustomPageControl. Then your implementation can be as simple as this:
import UIKit
class CustomPageControl: UIPageControl {
override var currentPage: Int {
didSet {
//Your code to play sounds based on selected index could go
//here, or broadcast a notification that your view controller
//would listen for
print("New page index = \(currentPage)")
}
}
}
Then just select the page control on your UIPageViewController, select the "Identity Inspector", and change the class of the page control to your custom CustomPageControl class. Once you've done that, whenever your page index changes, the didSet method above will be called.
I don't know why but delegates have always hurt my brain.
I have the delegate like so:
protocol StylistControllerDelegate {
func userInteractsWithTableView(tableViewOffset: CGFloat)
}
It sits inside StylistController and is called when the user scrolls:
class StylistController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate {
var delegate : StylistControllerDelegate?
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset.y
self.delegate?.userInteractsWithTableView(tableViewOffset: offset)
}
inside the cell I am attempting to set the CGFloat value to a variable:
class CollectionViewCell: UICollectionViewCell, StylistControllerDelegate {
//var stylistControllerTableViewOffset: CGFloat?
func userInteractsWithTableView(tableViewOffset: CGFloat) {
print(tableViewOffset)
if tableViewOffset > 100 {
collectionView.isScrollEnabled = true
} else {
collectionView.isScrollEnabled = false
}
}
}
I use the offset to enable and disable to ability to scroll of the collectionview INSIDE the cell. Once it gets to a certain value (in this case 100), the ability to scroll is enabled.
I am trying to get the scrollview of a uitableview that is static to get updated, I have a container view that will change sizes, and that works, but i cannot get the scrollable area to update.
I have tried setNeedsDisplay and setNeedsLayout and no luck.
Any ideas?
class TimeAwayRequestTableViewController: UITableViewController {
var originalHeight : CGFloat!
#IBOutlet var selectedDate : UIView!
#IBOutlet var calendarView: CalendarView!
override func viewDidLoad() {
super.viewDidLoad()
originalHeight = self.tableView.contentSize.height
calendarView.delegate = self
self.selectedDate.frame.size.height = CGFloat(SelectedDatesTimeAway.selectedDates.count * 44)
}
override func preferredContentSizeDidChangeForChildContentContainer(container: UIContentContainer) {
self.selectedDate.frame.size.height = CGFloat(SelectedDatesTimeAway.selectedDates.count * 44)
self.tableView.contentSize.height = self.originalHeight + self.selectedDate.frame.size.height
self.tableView.setNeedsDisplay()
self.tableView.setNeedsLayout()
}
}
extension TimeAwayRequestTableViewController : CalendarViewDelegate {
func calendarDidSelectDate(date: Moment) {
let theDate = date.date
SelectedDatesTimeAway.selectedDates.append(theDate)
print(SelectedDatesTimeAway.selectedDates.count)
let tbc = self.childViewControllers[0] as! UITableViewController
tbc.preferredContentSize.height = CGFloat ( SelectedDatesTimeAway.selectedDates.count * 44 )
print(tbc.preferredContentSize.height)
tbc.tableView.reloadData()
}
func calendarDidPageToDate(date: Moment) {
print(date)
}
}
If I understand the nature of your problem, you need to update the contentSize property of your UIScrollView when you change the content.
In a UIViewController, I instantiate a WKWebView and set the view controller as the WKWebiew scrollview's delegate:
class MyViewController: UIViewController, UIScrollViewDelegate {
var webView : WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView = WKWebView(frame: self.view.frame)
view = webView
webView.loadHTMLString("<h1>hello</h1>", baseURL: NSURL(string: "http://www.google.com")!)
webView.scrollView.delegate = self
}
func scrollViewDidScroll(scrollView: UIScrollView) {
println("didScroll")
}
The problem I have is that scrollViewDidScroll is called as soon as the WKWebView is created (ie without the user actually scrolling anything).
Why is that and how can I prevent it ?
Set a bool value to true in scrollViewWillBeginDragging method, which is called only when user scrolls and it is not automatically called by WKWebview
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
scrollByDragging = true
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !scrollByDragging{
UIView.animate(withDuration: 0.01) {
scrollView.setContentOffset(.zero, animated: true)
}
}
}
If you were to use contentOffset, you would be able to detect when the user scrolls.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y >= 0 {
print("didScroll")
}
}
If your UIScrollView is inside a UINavigationController, contentOffset.y will start at -44, but practically 0 is fine to use.