Swift UIText View replaceCharacters Out of bounds - swift

I try to replace link to image and show on TextView
This my code
class ViewController: UIViewController {
let textView: UITextView = {
let view = UITextView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(textView)
textView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
textView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
textView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
textView.topAnchor.constraint(equalTo: view.topAnchor, constant: 50).isActive = true
let input = "New iPhone \nhttp://cdn2.gsmarena.com/vv/bigpic/apple-iphone-6s1.jpg \nTest Test Test"
imageText(text: input)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func imageText(text:String) {
let attributedString = NSMutableAttributedString(string: text)
let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
let matches = detector?.matches(in: text, options: .reportCompletion, range:NSRange(location: 0, length: text.count))
for match in matches! {
if match.url?.absoluteString.suffix(3) == "jpg" {
let textAttachment = NSTextAttachment()
let data = NSData(contentsOf: (match.url!))
if data != nil{
let image = UIImage(data: data! as Data)
textAttachment.image = image
let attributedStringWithImage = NSAttributedString(attachment: textAttachment)
attributedString.replaceCharacters(in: (match.range), with: attributedStringWithImage)
}
}
}
textView.attributedText = attributedString
}
}
It works:
App Screen Shot
But if I add a second link, then it crashes.
let input = "New iPhone \nhttp://cdn2.gsmarena.com/vv/bigpic/apple-iphone-6s1.jpg \nTest Test Test \nhttp://cdn2.gsmarena.com/vv/bigpic/apple-iphone-6s1.jpg \nTest Test Test"
console
reason: 'NSMutableRLEArray replaceObjectsInRange:withObject:length:: Out of bounds'
I find the crash point is this line
attributedString.replaceCharacters(in: (match.range), with: attributedStringWithImage)
How to fix this problem?

The thing is: you are mutating attributedString in this line
attributedString.replaceCharacters(in: (match.range), with: attributedStringWithImage)
So, in the following iteration of the for loop, the range that you are trying to replace doesn't exist anymore.

Related

Swift Collection View always return first default value from Model

I have model which contain struct and array . Form Hit struct , I want to access tag property like this tagLabel.text = photoviewModel?.hits.tags, so that I can display the value of the property into label . For example when I click the first collection view cell , it should return image and label properly but problem is I got the correct image but always return same values for tag into label . The screenshot is added into below..
Here is the model .
import Foundation
struct Photo: Codable {
let total, totalHits: Int
let hits: [Hit]
}
struct Hit: Codable {
let id: Int
let pageURL: String
let type, tags: String
let previewURL: String
let previewWidth, previewHeight: Int
let webformatURL: String
let webformatWidth, webformatHeight: Int
let largeImageURL: String
let imageWidth, imageHeight, imageSize, views: Int
let downloads, collections, likes, comments: Int
let userID: Int
let user: String
let userImageURL: String
enum CodingKeys: String, CodingKey {
case id, pageURL, type, tags, previewURL, previewWidth, previewHeight, webformatURL, webformatWidth, webformatHeight, largeImageURL, imageWidth, imageHeight, imageSize, views, downloads, collections, likes, comments
case userID = "user_id"
case user, userImageURL
}
}
Here is the view Model .
import Foundation
class PhotoViewModel {
private let networkManager: NetworkManagerProtocol
var hits = [Hit]()
private var cache = [String: Data]()
// var net = NetworkManager()
weak var delegate: PhotoViewable?
init(networkManager: NetworkManagerProtocol) {
self.networkManager = networkManager
}
var rows: Int {
return hits.count
}
func fecthPhotoRecord(){
let networkUrl = NetworkURLs.baseURL
networkManager.getModel(Photo.self, from: networkUrl) { [weak self] result in
switch result{
case.success(let photo):
self?.hits = photo.hits
self?.delegate?.refreshUI()
case.failure(let error):
print(error)
self?.delegate?.showError()
}
}
}
func downloadImage(row: Int, completion: #escaping (Data) -> Void) {
let hit = hits[row]
let hitpath = hit.previewURL
if let data = cache[hitpath] {
completion(data)
return
}
networkManager
.getData(from: hitpath) { result in
switch result {
case .success(let data):
self.cache[hitpath] = data
DispatchQueue.main.async {
completion(data)
}
case .failure(let error):
print(error)
}
}
}
Here is the code in view controller .
import UIKit
class PhtotoDetailsViewController: UIViewController {
var photoviewModel : PhotoViewModel?
var peopleDetailsViewModel:PeopleDetailsViewModel?
private var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .fill
stackView.spacing = 5
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private let imageView: UIImageView = {
let imageView = UIImageView(frame: .zero)
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
private let tagLabel: UILabel = {
let label = UILabel(frame: .zero)
label.textAlignment = .center
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(stackView)
view.addSubview(tagLabel)
view.addSubview(imageView)
setUpUI()
setPhoto()
setContrain()
}
private func setContrain(){
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.topAnchor,constant: 100),
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant: -200),
stackView.leftAnchor.constraint(equalTo: view.leftAnchor , constant: 10),
stackView.rightAnchor.constraint(equalTo: view.rightAnchor,constant: 10),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor )
])
stackView.addArrangedSubview(imageView)
stackView.addArrangedSubview(tagLabel)
}
private func setUpUI(){
tagLabel.text = photoviewModel?.hits.first?.tags
}
var rowSelected = 0
private func setPhoto(){
photoviewModel?.downloadImage(row: rowSelected) { [weak self] data in
DispatchQueue.main.async {
let image = UIImage(data: data)
self?.imageView.image = image
}
}
}
}
I am having problem on this line ..
private func setUpUI(){
tagLabel.text = photoviewModel?.hits.first?.tags
}
Here is the screenshot when the app load first and it has image and label property ...
When I click the third cell form collection It has the correct image but correct label property .
In your setupUI() replace the line tagLabel.text = photoviewModel?.hits.first?.tags with this
tagLabel.text = photoviewModel?.hits[rowSelected].tags

User profile information is not showing up using swift and firestore database

pic of database
I am trying to show a label with the user's status on their profile page. after logging in the user gets presented with a VC that has a side menu. on that side menu is a "profile" option. once choosing this they go to their profile controller. right now i simply need to search users/current uid/ "MembershipStatus" and present this result into a label called "welcomeLabel".
I am returning nul
import UIKit
import Firebase
class NonMemberProfileController: UIViewController {
// MARK: - Properties
var welcomeLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = UIFont.systemFont(ofSize: 28)
label.translatesAutoresizingMaskIntoConstraints = false
label.alpha = 0
return label
}()
// MARK: - Init
override func viewDidLoad()
{
super.viewDidLoad()
authenticateUserAndConfigureView()
}
func loadUserData()
{
guard let uid = Auth.auth().currentUser?.uid else {return}
//.child("MembershipStatus")
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value) {
(snapshot) in
if snapshot.hasChild("MembershipStatus"){
print("true we have bingo")
} else {
print("no bueno")
dump(snapshot)
}
guard let status = snapshot.value as? String else { return }
self.welcomeLabel.text = "Welcome, \(status)"
print("this is lkjfdskjklsfad" + status)
UIView.animate(withDuration: 0.5, animations: {
self.welcomeLabel.alpha = 1
})
}
}
func authenticateUserAndConfigureView(){
if Auth.auth().currentUser == nil {
DispatchQueue.main.async {
let navController = UINavigationController(rootViewController: LoginViewController())
navController.navigationBar.barStyle = .black
self.present(navController, animated: true, completion: nil)
}
} else {
configureNavigationBar()
loadUserData()
}
}
// MARK: - Selectors
#objc func handleDismiss() {
dismiss(animated: true, completion: nil)
}
// MARK: - Helper Functions
func configureNavigationBar() {
view.backgroundColor = UIColor.lightGray
navigationItem.title = "Profile"
navigationController?.navigationBar.barTintColor = .darkGray
navigationController?.navigationBar.barStyle = .black
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "Home_2x").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleDismiss))
navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "baseline_settings_white_24dp").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleDismiss))
view.addSubview(welcomeLabel)
welcomeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
welcomeLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
You are using Cloud Firestore for data storage but your code is reading data form RealTime Database. You have to read data like this:
let userRef = Firestore.firestore().collection("users").document(uid)
userRef.getDocument { (documentSnapshot, error) in guard
let document = documentSnapshot?.data() else {
print(error)
return
}
print(document)
}

the function nextTapped does not move to next pdf page

function nextTapped and function previousTapped show this error " Use of unresolved identifier 'pdfView'; did you mean 'PDFView'? " I want from function nextTapped move to next pdf page and function previousTapped move to previous pdf page when i display pdf i want from nextButton move to next pdf page and previousButton move to previous pdf page
import UIKit
import AVFoundation
import MobileCoreServices
import PDFKit
class RecorderViewController: UIViewController {
#IBOutlet var nextButton:UIButton!
#IBOutlet var previousButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
nextButton.isHidden = true
previousButton.isHidden = true
}
#IBAction func `import`(_ sender: Any) {
let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypePDF as String], in: .import)
documentPicker.delegate = self as? UIDocumentPickerDelegate
documentPicker.allowsMultipleSelection = true
present(documentPicker, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func nextTapped(_ sender: Any) {
pdfView.goToNextPage(sender)}
#IBAction func previousTapped(_ sender: Any) {
pdfView.goToPreviousPage(sender)
}
extension RecorderViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let selectedFileURL = urls.first else {
return
}
/////////her to display PDF File
let pdfView = PDFView()
pdfView.frame = CGRect(x: 0, y: 160, width: 1024, height: 1139)
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
let thumbnailView = PDFThumbnailView()
thumbnailView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(thumbnailView)
thumbnailView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
thumbnailView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
thumbnailView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: thumbnailView.topAnchor).isActive = true
pdfView.heightAnchor.constraint(equalToConstant: 150)
pdfView.displayMode = .singlePage
// Fit content in PDFView.
nextButton.isHidden = false
previousButton.isHidden = false
pdfView.autoScales = true
pdfView.document = PDFDocument(url: selectedFileURL)
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let sandboxFileURL = dir.appendingPathComponent(selectedFileURL.lastPathComponent)
///i need to ensure the file is saved
if FileManager.default.fileExists(atPath: sandboxFileURL.path) {
print("Already exists! Do nothing")
}
else {
do {
try FileManager.default.copyItem(at: selectedFileURL, to: sandboxFileURL)
print("Copied file!")
}
catch {
print("Error: \(error)")
}
}
}
}
Define your pdfView in Class it self not in Extension like below :
//Add PDF View Object here not in extension
let pdfView = PDFView()
Just update your code slightly like this :
import UIKit
import AVFoundation
import MobileCoreServices
import PDFKit
class RecorderViewController: UIViewController {
//Add PDF View Object here not in extension
let pdfView = PDFView()
#IBOutlet var nextButton:UIButton!
#IBOutlet var previousButton:UIButton!
override func viewDidLoad() {
super.viewDidLoad()
nextButton.isHidden = true
previousButton.isHidden = true
}
#IBAction func `import`(_ sender: Any) {
let documentPicker = UIDocumentPickerViewController(documentTypes: [kUTTypePDF as String], in: .import)
documentPicker.delegate = self as? UIDocumentPickerDelegate
documentPicker.allowsMultipleSelection = true
present(documentPicker, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func nextTapped(_ sender: Any) {
pdfView.goToNextPage(sender)
}
#IBAction func previousTapped(_ sender: Any) {
pdfView.goToPreviousPage(sender)
}
}
extension RecorderViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard let selectedFileURL = urls.first else {
return
}
/////////her to display PDF File
pdfView.frame = CGRect(x: 0, y: 160, width: self.view.frame.size.width, height: self.view.frame.size.height - 160)
pdfView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
let thumbnailView = PDFThumbnailView()
thumbnailView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(thumbnailView)
thumbnailView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
thumbnailView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
thumbnailView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: thumbnailView.topAnchor).isActive = true
pdfView.heightAnchor.constraint(equalToConstant: 150)
pdfView.displayMode = .singlePage
// Fit content in PDFView.
nextButton.isHidden = false
previousButton.isHidden = false
pdfView.autoScales = true
pdfView.document = PDFDocument(url: selectedFileURL)
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let sandboxFileURL = dir.appendingPathComponent(selectedFileURL.lastPathComponent)
///i need to ensure the file is saved
if FileManager.default.fileExists(atPath: sandboxFileURL.path) {
print("Already exists! Do nothing")
}
else {
do {
try FileManager.default.copyItem(at: selectedFileURL, to: sandboxFileURL)
print("Copied file!")
}
catch {
print("Error: \(error)")
}
}
}
Hope this will solve your issue.

Create a Login Page for SKSprite Game

I am in the process of creating a game (Swift) in xcode using a number of SKScene and Sprite objects. I want to create a Scene (settings scene) that captures the player's name, email, gender etc. How can I go about this? How can I capture input from user. SKScenes do not allow input fields/values in the UI?
Thanks
You can build a custom login page that is conform with your game layout without try to rebuild in UIKit the same graphic assets.
Few days ago I've written an answer about SKSceneDelegate to communicate between the scene(SpriteKit) and the viewController (UIKit), take present this answer if you want to call other viewControllers because its the same concept of this answer..
Starting with this GameViewController we can develop some useful methods to handle the login form buttons and show some alerts:
import UIKit
import SpriteKit
class GameViewController: UIViewController, TransitionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
guard let view = self.view as! SKView? else { return }
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
let scene = GameScene(size:view.bounds.size)
scene.scaleMode = .fill
scene.delegate = self as TransitionDelegate
scene.anchorPoint = CGPoint.zero
view.presentScene(scene)
}
func showAlert(title:String,message:String) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok", style: .default) { action in
print("handle Ok action...")
})
alertController.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
self.present(alertController, animated: true)
}
func handleLoginBtn(username:String,password:String) {
print("handleLoginBtn")
print("username is: \(username) and password: \(password)")
}
func handleFacebookBtn() {
print("handleFacebookBtn")
}
func handleTwitterBtn() {
print("handleTwitterBtn")
}
}
Then we can make our scene trying to take the advantage of SpriteKit elements:
import SpriteKit
import UIKit
protocol TransitionDelegate: SKSceneDelegate {
func showAlert(title:String,message:String)
func handleLoginBtn(username:String,password:String)
func handleFacebookBtn()
func handleTwitterBtn()
}
class GameScene: SKScene,UITextFieldDelegate {
var usernameTextField:UITextField!
var passwordTextField:UITextField!
var loginBtn:SKShapeNode!
var facebookBtn:SKShapeNode!
var twitterBtn:SKShapeNode!
override func didMove(to view: SKView) {
//bg
let bg = SKSpriteNode(imageNamed: "appleWallpaper")
addChild(bg)
bg.position = CGPoint(x:self.size.width/2,y:self.size.height/2)
//title
let title = SKLabelNode.init(fontNamed: "AppleSDGothicNeo-Bold")
title.text = "xyzGame"; title.fontSize = 25
title.fontColor = .orange
addChild(title)
title.zPosition = 1
title.position = CGPoint(x:self.size.width/2,y:self.size.height-80)
//textfields
guard let view = self.view else { return }
let originX = (view.frame.size.width - view.frame.size.width/1.5)/2
usernameTextField = UITextField(frame: CGRect.init(x: originX, y: view.frame.size.height/4.5, width: view.frame.size.width/1.5, height: 30))
customize(textField: usernameTextField, placeholder: "Enter your username")
view.addSubview(usernameTextField)
usernameTextField.addTarget(self, action:#selector(GameScene.textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
passwordTextField = UITextField(frame: CGRect.init(x: originX, y: view.frame.size.height/4.5+60, width: view.frame.size.width/1.5, height: 30))
customize(textField: passwordTextField, placeholder: "Enter your password", isSecureTextEntry:true)
view.addSubview(passwordTextField)
//buttons
let myBlue = SKColor(colorLiteralRed: 59/255, green: 89/255, blue: 153/255, alpha: 1)
loginBtn = getButton(frame: CGRect(x:self.size.width/4,y:self.size.height/2,width:self.size.width/2,height:30),fillColor:myBlue,title:"Login",logo:nil,name:"loginBtn")
addChild(loginBtn)
loginBtn.zPosition = 1
let label = SKLabelNode.init(fontNamed: "AppleSDGothicNeo-Regular")
label.text = "or connect with"; label.fontSize = 15
label.fontColor = .gray
addChild(label)
label.zPosition = 1
label.position = CGPoint(x:self.size.width/2,y:self.size.height/2-30)
let logoFb = SKSpriteNode.init(imageNamed: "facebook-icon")
logoFb.setScale(0.5)
facebookBtn = getButton(frame: CGRect(x:self.size.width/4,y:self.size.height/2-80,width:self.size.width/4.5,height:30),fillColor:myBlue,logo:logoFb,name:"facebookBtn")
addChild(facebookBtn)
facebookBtn.zPosition = 1
let myCyan = SKColor(colorLiteralRed: 85/255, green: 172/255, blue: 239/255, alpha: 1)
let logoTw = SKSpriteNode.init(imageNamed: "twitter-icon")
logoTw.setScale(0.5)
twitterBtn = getButton(frame: CGRect(x:self.size.width/2,y:self.size.height/2-80,width:self.size.width/4.5,height:30),fillColor:myCyan,logo:logoTw,name:"twitterBtn")
addChild(twitterBtn)
twitterBtn.zPosition = 1
}
func customize(textField:UITextField, placeholder:String , isSecureTextEntry:Bool = false) {
let paddingView = UIView(frame:CGRect(x:0,y: 0,width: 10,height: 30))
textField.leftView = paddingView
textField.keyboardType = UIKeyboardType.emailAddress
textField.leftViewMode = UITextFieldViewMode.always
textField.attributedPlaceholder = NSAttributedString(string: placeholder,attributes: [NSForegroundColorAttributeName: UIColor.gray])
textField.autocapitalizationType = .none
textField.autocorrectionType = .no
textField.layer.borderColor = UIColor.gray.cgColor
textField.layer.borderWidth = 0.5
textField.layer.cornerRadius = 4.0
textField.textColor = .white
textField.isSecureTextEntry = isSecureTextEntry
textField.delegate = self
}
func getButton(frame:CGRect,fillColor:SKColor,title:String = "",logo:SKSpriteNode!,name:String)->SKShapeNode {
let btn = SKShapeNode(rect: frame, cornerRadius: 10)
btn.fillColor = fillColor
btn.strokeColor = fillColor
if let l = logo {
btn.addChild(l)
l.zPosition = 2
l.position = CGPoint(x:frame.origin.x+(frame.size.width/2),y:frame.origin.y+(frame.size.height/2))
l.name = name
}
if !title.isEmpty {
let label = SKLabelNode.init(fontNamed: "AppleSDGothicNeo-Regular")
label.text = title; label.fontSize = 15
label.fontColor = .white
btn.addChild(label)
label.zPosition = 3
label.position = CGPoint(x:frame.origin.x+(frame.size.width/2),y:frame.origin.y+(frame.size.height/4))
label.name = name
}
btn.name = name
return btn
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
switch name {
case "loginBtn":
self.run(SKAction.wait(forDuration: 0.1),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
(delegate as! TransitionDelegate).handleLoginBtn(username:self.usernameTextField.text!,password: self.passwordTextField.text!)
})
case "facebookBtn":
self.run(SKAction.wait(forDuration: 0.1),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
(delegate as! TransitionDelegate).handleFacebookBtn()
})
case "twitterBtn":
self.run(SKAction.wait(forDuration: 0.1),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
(delegate as! TransitionDelegate).handleTwitterBtn()
})
default:break
}
}
}
func textFieldDidChange(textField: UITextField) {
//print("everytime you type something this is fired..")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == usernameTextField { // validate email syntax
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
let result = emailTest.evaluate(with: textField.text)
let title = "Alert title"
let message = result ? "This is a correct email" : "Wrong email syntax"
if !result {
self.run(SKAction.wait(forDuration: 0.01),completion:{[unowned self] in
guard let delegate = self.delegate else { return }
(delegate as! TransitionDelegate).showAlert(title:title,message: message)
})
}
}
}
deinit {
print("\n THE SCENE \((type(of: self))) WAS REMOVED FROM MEMORY (DEINIT) \n")
}
}
Output:
Animated output:
As you can see we can handle both framework with their delegate methods, I've tested this page with iPhone 5 and iPhone 7 plus.

Swift 3 - how do I add clickable links to another view controller in the body of a TextView? [duplicate]

I am trying to display an attributed string in a UITextview with clickable links. I've created a simple test project to see where I'm going wrong and still can't figure it out. I've tried enabling user interaction and setting the shouldInteractWithURLs delegate method, but it's still not working. Here's my code (for a view controller that only contains a textview)
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let string = "Google"
let linkString = NSMutableAttributedString(string: string)
linkString.addAttribute(NSLinkAttributeName, value: NSURL(string: "https://www.google.com")!, range: NSMakeRange(0, string.characters.count))
linkString.addAttribute(NSFontAttributeName, value: UIFont(name: "HelveticaNeue", size: 25.0)!, range: NSMakeRange(0, string.characters.count))
textView.attributedText = linkString
textView.delegate = self
textView.selectable = true
textView.userInteractionEnabled = true
}
And here are the delegate methods I've implemented:
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
return false
}
func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool {
return true
}
This still isn't working. I've searched on this topic and nothing has helped yet. Thanks so much in advance.
Just select the UITextView in your storyboard and go to "Show Attributes inspector" and select selectable and links. As the image below shows. Make sure Editable is unchecked.
For swift3.0
override func viewDidLoad() {
super.viewDidLoad()
let linkAttributes = [
NSLinkAttributeName: NSURL(string: "http://stalwartitsolution.co.in/luminutri_flow/terms-condition")!
] as [String : Any]
let attributedString = NSMutableAttributedString(string: "Please tick box to confirm you agree to our Terms & Conditions, Privacy Policy, Disclaimer. ")
attributedString.setAttributes(linkAttributes, range: NSMakeRange(44, 18))
attributedString.addAttribute(NSUnderlineStyleAttributeName, value: NSNumber(value: 1), range: NSMakeRange(44, 18))
textview.delegate = self
textview.attributedText = attributedString
textview.linkTextAttributes = [NSForegroundColorAttributeName: UIColor.red]
textview.textColor = UIColor.white
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
return true
}
Swift 3 iOS 10: Here's Clickable extended UITextView that detect websites inside the textview automatically as long as the link start with www. for example: www.exmaple.com if it exist anywhere in the text will be clickable. Here's the class:
import Foundation
import UIKit
public class ClickableTextView:UITextView{
var tap:UITapGestureRecognizer!
override public init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
print("init")
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup(){
// Add tap gesture recognizer to Text View
tap = UITapGestureRecognizer(target: self, action: #selector(self.myMethodToHandleTap(sender:)))
// tap.delegate = self
self.addGestureRecognizer(tap)
}
func myMethodToHandleTap(sender: UITapGestureRecognizer){
let myTextView = sender.view as! UITextView
let layoutManager = myTextView.layoutManager
// location of tap in myTextView coordinates and taking the inset into account
var location = sender.location(in: myTextView)
location.x -= myTextView.textContainerInset.left;
location.y -= myTextView.textContainerInset.top;
// character index at tap location
let characterIndex = layoutManager.characterIndex(for: location, in: myTextView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
// if index is valid then do something.
if characterIndex < myTextView.textStorage.length {
let orgString = myTextView.attributedText.string
//Find the WWW
var didFind = false
var count:Int = characterIndex
while(count > 2 && didFind == false){
let myRange = NSRange(location: count-1, length: 2)
let substring = (orgString as NSString).substring(with: myRange)
// print(substring,count)
if substring == " w" || (substring == "w." && count == 3){
didFind = true
// print("Did find",count)
var count2 = count
while(count2 < orgString.characters.count){
let myRange = NSRange(location: count2 - 1, length: 2)
let substring = (orgString as NSString).substring(with: myRange)
// print("Did 2",count2,substring)
count2 += 1
//If it was at the end of textView
if count2 == orgString.characters.count {
let length = orgString.characters.count - count
let myRange = NSRange(location: count, length: length)
let substring = (orgString as NSString).substring(with: myRange)
openLink(link: substring)
print("It's a Link",substring)
return
}
//If it's in the middle
if substring.hasSuffix(" "){
let length = count2 - count
let myRange = NSRange(location: count, length: length)
let substring = (orgString as NSString).substring(with: myRange)
openLink(link: substring)
print("It's a Link",substring)
return
}
}
return
}
if substring.hasPrefix(" "){
print("Not a link")
return
}
count -= 1
}
}
}
func openLink(link:String){
if let checkURL = URL(string: "http://\(link.replacingOccurrences(of: " ", with: ""))") {
if UIApplication.shared.canOpenURL(checkURL) {
UIApplication.shared.open(checkURL, options: [:], completionHandler: nil)
print("url successfully opened")
}
} else {
print("invalid url")
}
}
public override func didMoveToWindow() {
if self.window == nil{
self.removeGestureRecognizer(tap)
print("ClickableTextView View removed from")
}
}
}