Swift 3, Display some lines of the HTML text - ios

I try to add html code inside webView:
webView.loadHTMLString(myHTML, baseURL: nil)
But last line displays cropped:
What is the best way to hide this cropped bottom line in my webView?

I recommend you too use UILabel
Full example
ViewController.swift
import UIKit
class ViewController: UIViewController {
var label: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.lightGray
var bounds = view.bounds
bounds.origin.y = 44
bounds.size.height = 100
label = UILabel(frame: bounds)
label?.backgroundColor = UIColor.white
view.addSubview(label!)
let filename = "HTMLPage.html"
if let path = Bundle.main.path(forResource: filename, ofType: nil) {
do {
let text = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
label?.attributedText = text.htmlToAttributedString
label?.numberOfLines = 5
label?.lineBreakMode = .byTruncatingTail
} catch {
print("Failed to read text from \(filename)")
}
} else {
print("Failed to load file from app bundle \(filename)")
}
}
}
extension String {
var htmlToAttributedString: NSAttributedString? {
do {
let data = self.data(using: String.Encoding.utf8, allowLossyConversion: true)
if let d = data {
let str = try NSAttributedString(data: d,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
return str
}
} catch {
}
return nil
}
}
HTMLPage.html
<div style="font-family:'Helvetica'; font-size: 11pt;"><div class="feed-description"><p>The apple tree (Malus pumila, commonly and erroneously called Malus domestica) is a deciduous tree in the rose family best known for its sweet, pomaceous fruit, the apple. link It is cultivated worldwide as a fruit tree, and is the most widely grown species in the genus Malus. The tree originated in Central Asia, where its wild ancestor, Malus sieversii, is still found today. Apples have been grown for thousands of years in Asia and Europe, and were brought to North America by European colonists. Apples have religious and mythological significance in many cultures, including Norse, Greek and European Christian traditions. link text text text</p></div></div>
Result

Related

Swift problem when adding annotations with regex to pdf page

I want to search for a regex in a pdf, and add annotations to it according, using the results from the regex. I have built a simple function that does this. As the amazing community (really amazing people who used their time helping me) posted I can I can use the decomposedStringWithCompatibilityMapping to search for the desired expression correctly in the pdf, but afterwards when I perform a pdf selection to find the bounds of it, I encounter a difference. I send you my code and some pictures.
func performRegex(regex:String, on pdfPage:PDFPage) {
guard let pdfString = pdfPage.string?.precomposedStringWithCanonicalMapping else { return }
guard let safeRegex = try? NSRegularExpression(pattern: regex, options: .caseInsensitive) else { return }
let results = safeRegex.matches(in: pdfString, options: .withoutAnchoringBounds, range: NSRange(pdfString.startIndex..., in: pdfString))
pdfPage.annotations.forEach { pdfPage.removeAnnotation($0)}
results.forEach { result in
let bbox = pdfPage.selection(for: result.range)?.bounds(for: pdfPage)
let annotation = PDFAnnotation(bounds: bbox!, forType: .highlight, withProperties: nil)
annotation.color = .yellow
annotation.contents = String(pdfString[Range(result.range, in:pdfString)!])
pdfPage.addAnnotation(annotation)
}
}
The problem is that when I do this and enter this expression [0-9] all my results are shifted:
While if I don't use precomposedStringWithCanonicalMapping, all my results are not shifted but I will encounter an error when I get a special character.
The problem (I suspect) is in this line of code.
let bbox = pdfPage.selection(for: result.range)?.bounds(for: pdfPage)
But I don't know any work arround for it.
Please if anyone can give me some help!
Thanks a lot
The only alternative I can think right now is to use the original string and fix the malformed ranges. Try like this:
var str = """
circular para poder realizar sus tareas laborales correspondientes a las actividades de comercialización de alimentos
"""
do {
let regex = try NSRegularExpression(pattern: ".", options: .caseInsensitive)
let results = regex.matches(in: str, options: .withoutAnchoringBounds, range: NSRange(location: 0, length: str.utf16.count))
var badrange: NSRange?
results.forEach { result in
guard let range = Range(result.range, in: str) else {
if badrange != nil {
badrange!.length += 1
if let range = Range(badrange!, in: str) {
let newStr = str[range]
print(newStr)
}
} else {
badrange = result.range
}
return
}
let newStr = str[range]
print(newStr)
badrange = nil
}
} catch {
print(error)
}

How to make sure that a string contains a whole word not just part of it

I need some code to make sure that if a whole word exists in a return formatted text file it is accepted and that, if only part of it is present, it is not considered.
If I type lau in the TextField it is accepted and I would rather the answer was false until a whole word is matched
Here is the file limited.txt I use in my project. Each word is on a separate line:
appetitive
appetitiveness
appetitost
appetize
appetized
appetizement
appetizer
appetizers
appetizing
appetizingly
appinite
appius
appl
applanate
applanation
applaud
applaudable
applaudably
applauded
applauder
applauders
applauding
applaudingly
applauds
applause
applauses
applausive
applausively
apple
appleberry
appleblossom
applecart
appled
appledrane
appledrone
applegrower
applejack
applejohn
applemonger
Thanks for your help
import SwiftUI
struct ContentView: View{
#ObservedObject var textFileStringContent: TexFileReader
#State private var text = ""
var body: some View{
VStack {
TextField("please type the word to check", text: $text)
// so that it does not matter if user capitalises a word
if textFileStringContent.data.contains(self.text.lowercased()) {
Text("part of it exists")
// I tried to code it in here but to no avail
// if it is a whole word {
// Text("congratulations it does exist")
// }
} else if !text.isEmpty {
Text("sorry no such word")
}
}.padding().font(.headline)
.navigationBarTitle("Word checker")
}
}
class TexFileReader: ObservableObject {
#Published var data: String = ""
init() { self.load(file: "limited") }
func load(file: String) {
if let filepath = Bundle.main.path(forResource: file, ofType: "txt") {
do {
let contents = try String(contentsOfFile: filepath)
DispatchQueue.main.async {
self.data = contents
print(self.data.contains("lau"))
// this prints true even if lau is not a whole word
// applaud
// applaudable
// applaudably
// applauded
// applauder
// applauders
// applauding
// applaudingly
// applauds
// applause
// applauses
// applausive
// applausively
// but present in each of these
// I need to make sure that the match is a whole word not just part of one
}
} catch let error as NSError {
print(error.localizedDescription)
}
} else {
print("File not found")
}
}
}
A possible way is to search with Regular Expression and the word boundary specifier \\b
if textFileStringContent.data.range(of: "\\b\(self.text)\\b", options: [.caseInsensitive, .regularExpression]) != nil {
You may check if it ends with a newline separator in your text file:
let textWithNewline = self.text.lowercased() + "\n"
if textFileStringContent.data.contains(textWithNewline) {
// it is a whole word
}
Foundation contains a language analysis engine NSLinguisticTagger which can do many things including finding specific words with locale sensitivity.
A simple implementation of what you're trying to do is:
//extension via https://stackoverflow.com/questions/15062458/shortcut-to-generate-an-nsrange-for-entire-length-of-nsstring/56391610#56391610
extension String {
func range(from nsRange: NSRange) -> Range<String.Index>? {
return Range(nsRange, in: self)
}
}
var tagger = NSLinguisticTagger(tagSchemes: [NSLinguisticTagScheme.tokenType], options: 0)
let baddata = """
applaud
applaudable
applaudably
applauded
applauder
applauders catlau
applauding
"""
let gooddata = """
applaud
applaudable
applaudably
applauded
applauder
applauders lau catlau
applauding
"""
var foundLau = false
tagger.string = baddata
tagger.enumerateTags(in: NSRange(location: 0, length: baddata.count), scheme: .tokenType, options: [.omitWhitespace]) { tag, tokenRange, _, _ in
if tag != nil, let range = baddata.range(from: tokenRange) {
let fragment = baddata[range]
if fragment.lowercased() == "lau" {
foundLau = true
}
}
}
print("found \"lau\" in baddata =", foundLau ? "true":"false")
tagger.string = gooddata
tagger.enumerateTags(in: NSRange(location: 0, length: gooddata.count), scheme: .tokenType, options: [.omitWhitespace]) { tag, tokenRange, _, _ in
if tag != nil, let range = gooddata.range(from: tokenRange) {
let fragment = gooddata[range]
if fragment.lowercased() == "lau" {
foundLau = true
}
}
}
print("found \"lau\" in gooddata =", foundLau ? "true":"false")
enumerateTags returns an NSRange which can be converted to Range for general Swift use.

How to show json data in line chart using swift

I am trying to design an ios app to display json data in a line chart.
First of all, this is my json data.
{
TH_5min: [
{
Data: "2019-02-23T00:00:00",
Time: "11:00:00",
XTP_A: 10.5, //temperature 1
XHP_A: 11.5, //humidity 1
XTP_B: 33.5,
XHP_B: 44.6,
XTP_C: 88.9,
XHP_C: 66.6,
XTP_D: 77.9,
XHP_D: 99.6,
XTP_E: 87.87,
XHP_E: 66.66
},
{
Data: "2019-02-23T00:00:00",
Time: "11:05:00",
XTP_A: 55.2, //temperature 1
XHP_A: 44.3, //humidity 1
XTP_B: 66.6,
XHP_B: 77.87,
XTP_C: 87.77,
XHP_C: 87.87,
XTP_D: 8.87,
XHP_D: 78.78,
XTP_E: 87.78,
XHP_E: 87.87
}
]
}
This is my implementation of the swift code showing json data.
override func viewDidLoad() {
super.viewDidLoad()
apiip = APIip
getlatestTh_5min()
#objc func getlatestTh_5min(){
guard let th_5minUrl = URL(string: "http://" + apiip + "/api/Th_5min") else{
return
}
let request = URLRequest(url: th_5minUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: {(data,response,error) -> Void in
if let error = error {
print(error)
return
}
if let data = data {
self.th_5mins = self.pardrJsonData(data: data)
self.getchat()
}
})
task.resume()
//getchat()
}
func pardrJsonData(data: Data) -> [Th_5min]{
var th_5mins = [Th_5min]()
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
let jsonTh_5mins = jsonResult?["TH_5min"] as! [AnyObject]
print(jsonTh_5mins)
print(th_5mins.count)
for jsonTh_5min in jsonTh_5mins{
var th_5min = Th_5min()
th_5min.Data = jsonTh_5min["Data"] as! String
th_5min.Time = jsonTh_5min["Time"] as! String
th_5min.XTP_A = jsonTh_5min["XTP_A"] as! Double
th_5min.XHP_A = jsonTh_5min["XHP_A"] as! Double
print(th_5min)
th_5mins.append(th_5min)
//getchat()
} }catch{
print(error)
}
//getchat()
return th_5mins
}
This is how I draw the line chart, using swift code.
#objc func getchat(){
chartView = LineChartView()
chartView.frame = CGRect(x: 20, y: 80, width: self.view.bounds.width-20,height: self.view.bounds.height-100)
self.view.addSubview(chartView)
var dataEntries1 = [ChartDataEntry]()
for i in 0..<th_5mins.count {
chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: [th_5mins[i].Time])
let y = th_5mins[i].XTP_A
let entry = ChartDataEntry.init(x: Double(i), y: Double(y))
dataEntries1.append(entry)
}
let chartDataSet1 = LineChartDataSet(entries: dataEntries1, label: "temperature")
chartDataSet1.colors = [NSUIColor.red]
var dataEntries2 = [ChartDataEntry]()
for i in 0..<th_5mins.count {
chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: [th_5mins[i].Time])
let y = th_5mins[i].XHP_A
let entry = ChartDataEntry.init(x: Double(i), y: Double(y))
dataEntries2.append(entry)
}
let chartDataSet2 = LineChartDataSet(entries: dataEntries2, label: "humidity")
chartDataSet2.colors = [NSUIColor.black]
let chartData = LineChartData(dataSets: [chartDataSet1, chartDataSet2])
chartView.data = chartData
}
}
This is the result of my work.
enter image description here
Although the json data is successfully displayed, I don't know why it is loading for a long time, and I hope that the "time" in my json data can be displayed on the X axis above, marked with my temperature and humidity, and cannot be successful.
I also hope that my line chart view can be implemented as a layout.
"I don't know why it is loading for a long time". Do you mean that the graph does not load immediately upon opening the view? This is because the data is loading asynchronously from a remote source (correctly now, well done). It may well take a few seconds for your JSON to download over the web. That is ok. You can test the endpoint in a browser and see how long the response takes.
"I hope that the 'time' in my json data can be displayed on the X axis above". Yes. You can take the assignation of IndexAxisValueFormatter outside of the loop and you should pass all labels as values into the constructor. Try this code, replacing the equivalent loop:-
var labels: [String] = []
for i in 0..<th_5mins.count {
let y = th_5mins[i].XTP_A
let entry = ChartDataEntry.init(x: Double(i), y: Double(y))
dataEntries1.append(entry)
labels.append(th_5mins[i].Time)
}
chartView.xAxis.valueFormatter = IndexAxisValueFormatter(values: labels)
Note that the method you use for plotting against dates will result in an evenly spread graph, irrespective of your time gaps (e.g. if readings are 5 mins apart between the first two, but 5 years apart for the next two, they will still appear with even gaps between them.

Swift code to produce a number of possible anagrams from a selected word

I've attempted to research ways to take a given word and calculate the number of possible anagrams a user can make from that word eg an 8 letter word such as snowbanks has 5 eight letter possibilities, 25 seven letter possibilities, etc (those are made up numbers). My initial plan would be to iterate over a dictionary list and check each of the words to see if it is an anagram of the word in question as I've seen suggested in other places.
Rearrange Letters from Array and check if arrangement is in array
seemed very promising, except that it is in objective C and when I tried to convert it to Swift using Swiftify I couldn't get it to work as shown below:
func findAnagrams() -> Set<AnyHashable>? {
let nineCharacters = [unichar](repeating: 0, count: 8)
let anagramKey = self.anagramKey()
// make sure this word is not too long/short.
if anagramKey == nil {
return nil
}
(anagramKey as NSString?)?.getCharacters(nineCharacters, range: NSRange)
let middleCharPos = Int((anagramKey as NSString?)?.range(of: (self as NSString).substring(with: NSRange)).location ?? 0)
var anagrams = Set<AnyHashable>()
// 0x1ff means first 9 bits set: one for each character
for i in 0...0x1ff {
// skip permutations that do not contain the middle letter
if (i & (1 << middleCharPos)) == 0 {
continue
}
var length: Int = 0
var permutation = [unichar](repeating: 0, count: 9)
for bit in 0...9 {
if true {
permutation[length] = nineCharacters[bit]
length += 1
}
}
if length < 4 {
continue
}
let permutationString = String(permutation)
let matchingAnagrams = String.anagramMap()[permutationString] as? [Any]
for word: String? in matchingAnagrams {
anagrams.insert(word)
}
}
return anagrams
}
class func anagramMap() -> [AnyHashable: Any]? {
var anagramMap: [AnyHashable: Any]
if anagramMap != nil {
return anagramMap
}
// this file is present on Mac OS and other unix variants
let allWords = try? String(contentsOfFile: "/usr/share/dict/words", encoding: .utf8)
var map = [AnyHashable: Any]()
autoreleasepool {
allWords?.enumerateLines(invoking: {(_ word: String?, _ stop: UnsafeMutablePointer<ObjCBool>?) -> Void in
let key = word?.anagramKey()
if key == nil {
return
}
var keyWords = map[key] as? [AnyHashable]
if keyWords == nil {
keyWords = [AnyHashable]()
map[key] = keyWords
}
if let aWord = word {
keyWords?.append(aWord)
}
})
}
anagramMap = map
return anagramMap
}
func anagramKey() -> String? {
let lowercaseWord = word.lowercased()
// make sure to take the length *after* lowercase. it might change!
let length: Int = lowercaseWord.count
// in this case we're only interested in anagrams 4 - 9 characters long
if length < 3 || length > 9 {
return nil
}
let sortedWord = [unichar](repeating: 0, count: length)
(lowercaseWord as NSString).getCharacters(sortedWord, range: NSRange)
qsort_b(sortedWord, length, MemoryLayout<unichar>.size, {(_ aPtr: UnsafeRawPointer?, _ bPtr: UnsafeRawPointer?) -> Int in
let a = Int(unichar(aPtr))
let b = Int(unichar(bPtr))
return b - a
})
return String(describing: sortedWord)
}
func isReal(word: String) -> Bool {
let checker = UITextChecker()
let range = NSMakeRange(0, word.utf16.count)
let misspelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")
return misspelledRange.location == NSNotFound
}
}
I've also tried the following in an attempt to just produce a list of words that I could iterate over to check for anagrams (I have working code that checks guesses vs the main word to check for anagrams) but I wasn't able to get them to work, possibly because they require a file to be copied to the app, since I was under the impression that the phone has a dictionary preloaded that I could use for words (although I may be mistaken):
var allTheWords = try? String(contentsOfFile: "/usr/share/dict/words", encoding: .utf8)
for line: String? in allTheWords?.components(separatedBy: "\n") ?? [String?]() {
print("\(line ?? "")")
print("Double Fail \(allTheWords)")
}
and
if let wordsFilePath = Bundle.main.path(forResource: "dict", ofType: nil) {
do {
let wordsString = try String(contentsOfFile: wordsFilePath)
let wordLines = wordsString.components(separatedBy: NSCharacterSet.newlines)
let randomLine = wordLines[Int(arc4random_uniform(UInt32(wordLines.count)))]
print(randomLine)
} catch { // contentsOfFile throws an error
print("Error: \(error)")
}
}
}
I looked at UIReferenceLibraryViewController as well in an attempt to use it to produce a list of words instead of defining a selected word, but the following isn't a valid option.
let words = UIReferenceLibraryViewController.enumerated
Any assistance would be greatly appreciated!

Make Clickable UILabel Using Swift

I want to Set Particular Word clickable in UILabel text using Swift.
Is it possible?
If more than one label is here how can I detect which word is pressed?
You can not do with the simple label.
There is library available in the github.
https://github.com/TTTAttributedLabel/TTTAttributedLabel
From this you can use the method called yourLabel.addLinkToURL()
class ViewController: UIViewController , TTTAttributedLabelDelegate{
#IBOutlet var lbl: TTTAttributedLabel!
override func viewDidLoad() {
super.viewDidLoad()
var str : NSString = "Hello this is link"
lbl.delegate = self
lbl.text = str as String
var range : NSRange = str.rangeOfString("link")
lbl.addLinkToURL(NSURL(string: "http://github.com/mattt/")!, withRange: range)
}
func attributedLabel(label: TTTAttributedLabel!, didSelectLinkWithURL url: NSURL!) {
UIApplication.sharedApplication().openURL(url)
}
}
SWIFT 3.0
privacyLabel.delegate = self
let strPolicy : NSString = "Agree to the Terms & Conditions"
privacyLabel.text = strPolicy as String
let range1 : NSRange = strPolicy.range(of: "Terms & Conditions")
privacyLabel.addLink(to: URL(string: "http://Terms.com")!, with: range1)
func attributedLabel(_ label: TTTAttributedLabel!, didSelectLinkWith url: URL!) {
print("url \(url)")
// UIApplication.sharedApplication().openURL(url)
}
I'd like to share my library https://github.com/psharanda/Atributika
It contains modern replacement of TTTAtributedLabel + powerful set of methods to detect and style different stuff like tags, hashtags, mentions etc (everything of that can be clickable)
Some code to show how it works:
let link = Style
.font(.boldSystemFont(ofSize: 14))
.foregroundColor(.black)
.foregroundColor(.red, .highlighted)
let tos = link.named("tos")
let pp = link.named("pp")
let all = Style
.font(.systemFont(ofSize: 14))
.foregroundColor(.gray)
let text = "<tos>Terms of Service</tos> and <pp>Privacy Policy</pp>"
.style(tags: tos, pp)
.styleAll(all)
let tosLabel = AttributedLabel()
tosLabel.textAlignment = .center
tosLabel.attributedText = text
tosLabel.onClick = { label, detection in
switch detection.type {
case .tag(let tag):
switch tag.name {
case "pp":
print("Privacy Policy clicked")
case "tos":
print("Terms of Service clicked")
default:
break
}
default:
break
}
}
view.addSubview(tosLabel)

Resources