Render UIWebView inside UITableViewCell - ios

Can someone tell me what am I doing wrong?
I have a tableCell and inside it I have a stackView and inside it a UIWebview added programatically.
class ChatTableViewCell: UITableViewCell {
#IBOutlet var stackView: UIStackView!
func setupCell(message: ChatMessage) {
addHtmlToView(msg: "<p>This is <b>bold text</b></p>")
}
func addHtmlToView(msg: String) {
let webView = UIWebView()
webView.loadHTMLString("<html><body><div id='mainHtml'>" + msg + "</div></body></html>", baseURL: nil)
webView.backgroundColor = UIColor.clear
webView.isOpaque = false
webView.delegate = self
webView.scrollView.isScrollEnabled = false
webView.scrollView.bounces = false
webView.backgroundColor = .red
webView.scrollView.contentInset = UIEdgeInsets(top: -8, left: -8, bottom: -8, right: -8)
webView.translatesAutoresizingMaskIntoConstraints = false
self.stackView.addArrangedSubview(webView)
heightWebViewConstraint = NSLayoutConstraint(item: webView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 10)
widthWebViewConstraint = NSLayoutConstraint(item: webView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 1)
webView.addConstraint(heightWebViewConstraint)
webView.addConstraint(widthWebViewConstraint)
NSLayoutConstraint.activate([ heightWebViewConstraint, widthWebViewConstraint])
}
}
extension ChatTableViewCell: UIWebViewDelegate {
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool {
if navigationType == UIWebView.NavigationType.linkClicked {
UIApplication.shared.openURL(request.url!)
return false
}
return true
}
func webViewDidFinishLoad(_ webView: UIWebView) {
webView.frame.size.height = 1.0
webView.sizeToFit()
let result = NumberFormatter().number(from: webView.stringByEvaluatingJavaScript(from: "document.getElementById('mainHtml').offsetHeight") ?? "0")
heightWebViewConstraint.constant = 200
}
}
After this the cell render like this
Next I will use the value from "result" but this doesn't work with an hardcoded value either

I think you have to Add Distribution property of Stack view as "Fill Equally"

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 Alamofire Graph issue

I am new to IOS development. Please help me with issue. I am not getting data on Y axis. Fetching data through Alamofire using webAPI. Data is coming nil. Same code is working fine with UIViewController table. I am getting the response. But when using in Graph it is not working.
Issue: I am using Alamofire to parse WebAPI. 'SLPercent' is the value which i want to display on Y-axis.But xml value coming as nil.
class ViewController: UIViewController, ScrollableGraphViewDataSource {
var xml = try! XML.parse("")
var graphView: ScrollableGraphView!
var currentGraphType = GraphType.bar
var graphConstraints = [NSLayoutConstraint]()
var label = UILabel()
var reloadLabel = UILabel()
// Data for the different plots
var numberOfDataItems = day
// Data for graphs with a single plot
/*lazy var simpleLinePlotData: [Double] = self.generateRandomData(self.numberOfDataItems!, max: 100, shouldIncludeOutliers: false)
*/
lazy var barPlotData: [Double] = self.generateRandomData(self.numberOfDataItems!, max: 100, shouldIncludeOutliers: false)
// Data for graphs with multiple plots
lazy var blueLinePlotData: [Double] = self.generateRandomData(self.numberOfDataItems!, max: 50)
// lazy var orangeLinePlotData: [Double] = self.generateRandomData(self.numberOfDataItems!, max: 40, shouldIncludeOutliers: false)
// Init
override func viewDidLoad() {
super.viewDidLoad()
let user = "ndbd#gmail.com"
let passwort = "xdc"
var url = URL(string: "https://ceef")
let configuration = URLSessionConfiguration.default
configuration.requestCachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData
configuration.timeoutIntervalForResource = 10
let credentialData = "\(user):\(passwort)".data(using: String.Encoding.utf8)!
let base64Credentials = credentialData.base64EncodedString(options: [])
let headers = ["Accept": "application/xml","Authorization": "Basic \(base64Credentials)"]
DispatchQueue.main.async {
Alamofire.request(
url!,
method: .get,
parameters: nil,
encoding: URLEncoding.default,
headers:headers)
.responseString
{ response in
debugPrint(response)
print(response.request) // original URL request
print(response.response) // HTTP URL response
print(response.data) // server data
print(response.result) // result of response serialization
if response.result.value != nil
{
self.xml = try! XML.parse(response.result.value!)
}
}
}
// self.CallWebAPI()
// Labels for the x-axis
let now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "LLLL"
let nameOfMonth = dateFormatter.string(from: now)
var xAxisLabels: [String] = self.generateSequentialLabels(self.numberOfDataItems!, text: nameOfMonth);
graphView = createMultiPlotGraphOne(self.view.frame)
graphView.topMargin = 200
graphView.bottomMargin = 20
addReloadLabel(withText: "RELOAD")
self.view.insertSubview(graphView, belowSubview: reloadLabel)
setupConstraints()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "⬅", style: .plain, target: self, action: #selector(backAction))
}
func backAction()
{
//print("Back Button Clicked")
dismiss(animated: true, completion: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewDidLoad()
// self.CallWebAPI()
graphView = createMultiPlotGraphOne(self.view.frame)
graphView.topMargin = 200
graphView.bottomMargin = 20
addReloadLabel(withText: "RELOAD")
self.view.insertSubview(graphView, belowSubview: reloadLabel)
setupConstraints()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidLoad()
graphView = createMultiPlotGraphOne(self.view.frame)
graphView.topMargin = 200
graphView.bottomMargin = 20
addReloadLabel(withText: "RELOAD")
self.view.insertSubview(graphView, belowSubview: reloadLabel)
setupConstraints()
}
// Implementation for ScrollableGraphViewDataSource protocol
// #########################################################
// You would usually only have a couple of cases here, one for each
// plot you want to display on the graph. However as this is showing
// off many graphs with different plots, we are using one big switch
// statement.
func value(forPlot plot: Plot, atIndex pointIndex: Int) -> Double {
switch("bar") {
// Data for the graphs with a single plot
case "bar":
return barPlotData[pointIndex]
default:
return 30
}
}
func label(atIndex pointIndex: Int) -> String {
// Ensure that you have a label to return for the index
let now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "LLLL"
let nameOfMonth = dateFormatter.string(from: now)
var xAxisLabels: [String] = self.generateSequentialLabels(self.numberOfDataItems!, text: nameOfMonth);
return xAxisLabels[pointIndex]
}
func numberOfPoints() -> Int {
return numberOfDataItems!
}
// Creating Different Kinds of Graphs
// min: 0
// max: 100
// Will not adapt min and max reference lines to range of visible points
private func createBarGraph(_ frame: CGRect) -> ScrollableGraphView {
let graphView = ScrollableGraphView(frame: frame, dataSource: self)
graphView.topMargin = 200
graphView.bottomMargin = 20
// Setup the plot
let barPlot = BarPlot(identifier: "bar")
barPlot.barWidth = 25
barPlot.barLineWidth = 1
barPlot.barLineColor = UIColor.colorFromHex(hexString: "#777777")
barPlot.barColor = UIColor.colorFromHex(hexString: "#555555")
barPlot.adaptAnimationType = ScrollableGraphViewAnimationType.elastic
barPlot.animationDuration = 1.5
// Setup the reference lines
let referenceLines = ReferenceLines()
referenceLines.referenceLineLabelFont = UIFont.boldSystemFont(ofSize: 8)
referenceLines.referenceLineColor = UIColor.white.withAlphaComponent(0.2)
referenceLines.referenceLineLabelColor = UIColor.white
referenceLines.dataPointLabelColor = UIColor.white.withAlphaComponent(0.5)
// Setup the graph
graphView.backgroundFillColor = UIColor.colorFromHex(hexString: "#333333")
graphView.shouldAnimateOnStartup = true
graphView.rangeMax = 100
graphView.rangeMin = 0
// Add everything
graphView.addPlot(plot: barPlot)
graphView.addReferenceLines(referenceLines: referenceLines)
return graphView
}
fileprivate func createMultiPlotGraphOne(_ frame: CGRect) -> ScrollableGraphView {
let graphView = ScrollableGraphView(frame: frame, dataSource: self)
graphView.topMargin = 200
graphView.bottomMargin = 20
// Setup the first plot.
let blueLinePlot = LinePlot(identifier: "multiBlue")
blueLinePlot.lineColor = UIColor.colorFromHex(hexString: "#16aafc")
blueLinePlot.adaptAnimationType = ScrollableGraphViewAnimationType.elastic
// dots on the line
let blueDotPlot = DotPlot(identifier: "multiBlueDot")
blueDotPlot.dataPointType = ScrollableGraphViewDataPointType.circle
blueDotPlot.dataPointSize = 5
blueDotPlot.dataPointFillColor = UIColor.colorFromHex(hexString: "#16aafc")
blueDotPlot.adaptAnimationType = ScrollableGraphViewAnimationType.elastic
// Setup the reference lines.
let referenceLines = ReferenceLines()
referenceLines.referenceLineLabelFont = UIFont.boldSystemFont(ofSize: 8)
referenceLines.referenceLineColor = UIColor.white.withAlphaComponent(0.2)
referenceLines.referenceLineLabelColor = UIColor.white
referenceLines.relativePositions = [0, 0.2, 0.4, 0.6, 0.8, 1]
referenceLines.dataPointLabelColor = UIColor.white.withAlphaComponent(1)
// Setup the graph
graphView.backgroundFillColor = UIColor.colorFromHex(hexString: "#333333")
graphView.dataPointSpacing = 80
graphView.shouldAnimateOnStartup = true
graphView.shouldAdaptRange = true
graphView.shouldRangeAlwaysStartAtZero = true
// Add everything to the graph.
graphView.addReferenceLines(referenceLines: referenceLines)
graphView.addPlot(plot: blueLinePlot)
graphView.addPlot(plot: blueDotPlot)
return graphView
}
// Constraints and Helper Functions
// ################################
private func setupConstraints() {
self.graphView.translatesAutoresizingMaskIntoConstraints = false
graphConstraints.removeAll()
let topConstraint = NSLayoutConstraint(item: self.graphView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
let rightConstraint = NSLayoutConstraint(item: self.graphView, attribute: NSLayoutAttribute.right, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.right, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: self.graphView, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0)
let leftConstraint = NSLayoutConstraint(item: self.graphView, attribute: NSLayoutAttribute.left, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.left, multiplier: 1, constant: 0)
graphConstraints.append(topConstraint)
graphConstraints.append(bottomConstraint)
graphConstraints.append(leftConstraint)
graphConstraints.append(rightConstraint)
self.view.addConstraints(graphConstraints)
}
// Adding and updating the graph switching label in the top right corner of the screen.
private func addLabel(withText text: String) {
label.removeFromSuperview()
label = createLabel(withText: text)
label.isUserInteractionEnabled = true
let rightConstraint = NSLayoutConstraint(item: label, attribute: NSLayoutAttribute.right, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.right, multiplier: 1, constant: -20)
let topConstraint = NSLayoutConstraint(item: label, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 80)
let heightConstraint = NSLayoutConstraint(item: label, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 40)
let widthConstraint = NSLayoutConstraint(item: label, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: label.frame.width * 1.5)
let tapGestureRecogniser = UITapGestureRecognizer(target: self, action: #selector(didTap))
label.addGestureRecognizer(tapGestureRecogniser)
self.view.insertSubview(label, aboveSubview: reloadLabel)
self.view.addConstraints([rightConstraint, topConstraint, heightConstraint, widthConstraint])
}
private func addReloadLabel(withText text: String) {
reloadLabel.removeFromSuperview()
reloadLabel = createLabel(withText: text)
reloadLabel.isUserInteractionEnabled = true
let leftConstraint = NSLayoutConstraint(item: reloadLabel, attribute: NSLayoutAttribute.left, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.left, multiplier: 1, constant: 20)
let topConstraint = NSLayoutConstraint(item: reloadLabel, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 80)
let heightConstraint = NSLayoutConstraint(item: reloadLabel, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 40)
let widthConstraint = NSLayoutConstraint(item: reloadLabel, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: reloadLabel.frame.width * 1.5)
let tapGestureRecogniser = UITapGestureRecognizer(target: self, action: #selector(reloadDidTap))
reloadLabel.addGestureRecognizer(tapGestureRecogniser)
self.view.insertSubview(reloadLabel, aboveSubview: graphView)
self.view.addConstraints([leftConstraint, topConstraint, heightConstraint, widthConstraint])
}
private func createLabel(withText text: String) -> UILabel {
let label = UILabel()
label.backgroundColor = UIColor.black.withAlphaComponent(0.5)
label.text = text
label.textColor = UIColor.white
label.textAlignment = NSTextAlignment.center
label.font = UIFont.boldSystemFont(ofSize: 14)
label.layer.cornerRadius = 2
label.clipsToBounds = true
label.translatesAutoresizingMaskIntoConstraints = false
label.sizeToFit()
return label
}
// Button tap events
func didTap(_ gesture: UITapGestureRecognizer) {
currentGraphType.next()
self.view.removeConstraints(graphConstraints)
graphView.removeFromSuperview()
switch(currentGraphType) {
case .bar:
graphView = createBarGraph(self.view.frame)
addReloadLabel(withText: "RELOAD")
addLabel(withText: "BAR")
}
self.view.insertSubview(graphView, belowSubview: reloadLabel)
setupConstraints()
}
func reloadDidTap(_ gesture: UITapGestureRecognizer) {
// TODO: Currently changing the number of data items is not supported.
// It is only possible to change the the actual values of the data before reloading.
// numberOfDataItems = 30
// data for graphs with a single plot
barPlotData = self.generateRandomData(self.numberOfDataItems!, max: 100, shouldIncludeOutliers: false)
blueLinePlotData = self.generateRandomData(self.numberOfDataItems!, max: 50)
let now = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "LLLL"
let nameOfMonth = dateFormatter.string(from: now)
var xAxisLabels: [String] = self.generateSequentialLabels(self.numberOfDataItems!, text: nameOfMonth);
xAxisLabels = self.generateSequentialLabels(self.numberOfDataItems!, text: nameOfMonth)
graphView.reload()
}
// Data Generation
private func generateRandomData(_ numberOfItems: Int, max: Double, shouldIncludeOutliers: Bool = true) -> [Double] {
var data = [Double]()
var counter = 1
for _ in 0 ..< numberOfItems
{
for Result in xml["WebAPiResponse","Result"]
{
let SLPercent = Result["SLPercent"].text!;
let Date = Result["DateCST"].text!;
let DateFromService = Int(Date.substring(to:Date.index(Date.startIndex, offsetBy: 2)))
if (counter == DateFromService!)
{
data.append(Double(SLPercent)!)
}
}
data.append(Double(counter))
counter = counter + 1;
}
return data
}
private func generateRandomData(_ numberOfItems: Int, variance: Double, from: Double) -> [Double] {
var data = [Double]()
for _ in 0 ..< numberOfItems {
let randomVariance = Double(arc4random()).truncatingRemainder(dividingBy: variance)
var randomNumber = from
if(arc4random() % 100 < 50) {
randomNumber += randomVariance
}
else {
randomNumber -= randomVariance
}
data.append(randomNumber)
}
return data
}
private func generateSequentialLabels(_ numberOfItems: Int, text: String) -> [String] {
var labels = [String]()
for i in 0 ..< numberOfItems {
labels.append("\(text) \(i+1)")
}
return labels
}
// The type of the current graph we are showing.
enum GraphType {
case bar
mutating func next() {
switch(self) {
case .bar:
self = GraphType.bar
}
}
}
override var prefersStatusBarHidden : Bool {
return true
}
}

How can I display just a part of a Webpage? UIWebView Swift

I am working on my iOS-App in Swift and I've already finished my Android-App. On Android, I already found a solution to display just a part of a webpage. This is what I want to do to my iOS-app, too.
So, for those who are familiar with android developing, it was this solution which helped me:
How to display a part of the webpage on the webview android
But I asked my question in a way that it is possible to answer this question even if you don't have experience with android developing!
You can assume that, as this android solution does, my webpage is darebee.com, in this case maybe on the workout-page, so I precisely mean:
https://darebee.com/wods.html
I want to show just one div class which shows the content of the page, in this case:
<div class="ja-products-wrapper products wrapper grid products-grid cols-3">
That is my code until now (working, but showing full page):
override func viewDidLoad() {
super.viewDidLoad()
let url = URL (string: "https://darebee.com/wods.html")
let request = URLRequest(url: url!)
webview.loadRequest(request)
}
I know there is also a way to hide some parts of the webpage, but it would be far more easier, elegant and shorter to just show this one div-class.
Is it possible to do this? It would be nice to get an answer which is helpful.
UPDATE:
I found a solution that might be helpful for me, but somehow it is not working.
Because the code was 2,5 years old, something I deprecated so I had to update the code to my own.
Maybe on the constraints part, there could be a mistake, but there are no errors at all. Also the backgroundColor is not visible although all the lines I wanted to print are printed.
Maybe you can look to my code and, if it helps you, compare it to the solution I found: How to only display part of web page content (swift)
This is my code:
import UIKit
import JavaScriptCore
import WebKit
class ViewController: UIViewController {
private weak var webView: WKWebView!
#IBOutlet weak var vclabel: UIView!
private var userContentController: WKUserContentController!
override func viewDidLoad() {
super.viewDidLoad()
createViews()
print("no prob1")
loadPage(urlString: "https://dribbble.com/", partialContentQuerySelector: ".dribbbles.group")
print("no prob2")
}
private func createViews() {
userContentController = WKUserContentController()
let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
let webView = WKWebView(frame: view.bounds, configuration: configuration)
webView.translatesAutoresizingMaskIntoConstraints = false
let color1 = hexStringToUIColor(hex: "#FFFFFF")
webView.backgroundColor = color1
if (webView.backgroundColor == color1){
print("color1")
}
let leadingConstraint = NSLayoutConstraint(item: webView, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.leading, multiplier: 1, constant: 0)
let topConstraint = NSLayoutConstraint(item: webView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: topLayoutGuide, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: webView, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.trailing, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: webView, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: vclabel, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
view.addSubview(webView)
NSLayoutConstraint.activate([leadingConstraint, topConstraint, trailingConstraint, bottomConstraint])
self.webView = webView
}
private func loadPage(urlString: String, partialContentQuerySelector selector: String) {
userContentController.removeAllUserScripts()
print("load")
let userScript = WKUserScript(source: scriptWithDOMSelector(selector: selector),
injectionTime: WKUserScriptInjectionTime.atDocumentEnd,
forMainFrameOnly: true)
userContentController.addUserScript(userScript)
let url = NSURL(string: urlString)!
webView.load(NSURLRequest(url: url as URL) as URLRequest)
print("loaded")
}
private func scriptWithDOMSelector(selector: String) -> String {
print("DOMSelector")
let script =
"var selectedElement = document.querySelector('\(selector)');" +
"document.body.innerHTML = selectedElement.innerHTML;"
return script
}
func hexStringToUIColor (hex:String) -> UIColor {
var cString:String = hex.trimmingCharacters(in: .whitespacesAndNewlines).uppercased()
if (cString.hasPrefix("#")) {
cString.remove(at: cString.startIndex)
}
if ((cString.characters.count) != 6) {
return UIColor.gray
}
var rgbValue:UInt32 = 0
Scanner(string: cString).scanHexInt32(&rgbValue)
return UIColor(
red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
alpha: CGFloat(1.0)
)
}
}
And this is my output:
Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
color1
no prob1
load
DOMSelector
loaded
no prob2
I just don't know what else I should do to get an answer to this question.
Anyone out there who can help me in any way?
Use LoadHtmlString like this:
let requestURL: URL? = yourWebView.request().url
var error: Error?
let page = try? String(contentsOf: requestURL, encoding: String.Encoding.ascii)
yourWebView.loadHTMLString(page!, baseURL: requestURL)

how could I add a uiactivity indicator in in bottom/footer of collection view?

i am new to swift . i can not figure how can i do it . basically i need to show uiactivity indicator when collection view loaded all data then you try to scrolling then showing uiactivity indicator . it mean load more data from web just wait .
what i did .
///define
var indicatorFooter : UIActivityIndicatorView!
//set up UIActivityIndicatorView to the collection view
override func setupViews() {
indicatorFooter = UIActivityIndicatorView(frame: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(collectionView.frame.width), height: CGFloat(44)))
indicatorFooter.color = UIColor.black
collectionView.addSubview(indicatorFooter)
}
//end of the scroll view then load next 20 data from api
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
// UITableView only moves in one direction, y axis
let currentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
// Change 10.0 to adjust the distance from bottom
if maximumOffset - currentOffset <= 10.0 {
self.loadMore()
}
}
//request web to down load data
func loadMore(){
//but it is not working indicator
indicatorFooter.startAnimating()
}
Add the following extension to your code:
extension UIView {
// MARK: Activity Indicator
func activityIndicator(show: Bool) {
activityIndicator(show: show, style: .gray)
}
func activityIndicator(show: Bool, style: UIActivityIndicatorViewStyle) {
var spinner: UIActivityIndicatorView? = viewWithTag(NSIntegerMax - 1) as? UIActivityIndicatorView
if spinner != nil {
spinner?.removeFromSuperview()
spinner = nil
}
if spinner == nil && show {
spinner = UIActivityIndicatorView.init(activityIndicatorStyle: style)
spinner?.translatesAutoresizingMaskIntoConstraints = false
spinner?.hidesWhenStopped = true
spinner?.tag = NSIntegerMax - 1
if Thread.isMainThread {
spinner?.startAnimating()
} else {
DispatchQueue.main.async {
spinner?.startAnimating()
}
}
insertSubview((spinner)!, at: 0)
NSLayoutConstraint.init(item: spinner!, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: 0.0).isActive = true
NSLayoutConstraint.init(item: spinner!, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0.0).isActive = true
spinner?.isHidden = !show
}
}
}
To add a spinner to your collectionView you would simply call:
collectionView.activityIndicator(show: true)

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]];

Resources