I'm trying to build an app where I input text into a textField and get the auto predict that google provides with its Fetcher GooglePlaces API. I have copied the code and altered it for my app but I can not figure out what I am doing wrong. I am using Swift Xcode8 Beta 3.
Please see my code below.
import UIKit
import GooglePlaces
import GoogleMaps
class NewTripAddCitiesViewController: UIViewController {
var fetcher: GMSAutocompleteFetcher?
#IBOutlet var textView: UITextView!
#IBOutlet var placesTextField: UITextField!
#IBOutlet var addToTripLabel: UIButton!
#IBOutlet var addCitiesLabel: UILabel!
#IBAction func addPlaceToTable(_ sender: AnyObject) {
}
#IBAction func savePlaces(_ sender: AnyObject) {
}
override func viewDidLoad() {
super.viewDidLoad()
addCitiesLabel.text = "Please select which places you'd like to visit in \(tripCountry):"
addToTripLabel.setTitle("Add to \"\(tripName)\"", for: [])
self.view.backgroundColor = UIColor.white()
self.edgesForExtendedLayout = []
// Set bounds to inner-west Sydney Australia.
let neBoundsCorner = CLLocationCoordinate2D(latitude: -33.843366, longitude: 151.134002)
let swBoundsCorner = CLLocationCoordinate2D(latitude: -33.875725, longitude: 151.200349)
let bounds = GMSCoordinateBounds(coordinate: neBoundsCorner, coordinate: swBoundsCorner)
// Set up the autocomplete filter.
let filter = GMSAutocompleteFilter()
filter.type = .establishment
// Create the fetcher.
fetcher = GMSAutocompleteFetcher(bounds: bounds, filter: filter)
fetcher!.delegate = self
placesTextField = UITextField(frame: CGRect(x: 5.0, y: 0, width: self.view.bounds.size.width - 5.0, height: 44.0))
placesTextField?.autoresizingMask = .flexibleWidth
placesTextField?.addTarget(self, action: Selector(("textFieldDidChange:")), for: .editingChanged)
textView = UITextView(frame: CGRect(x: 0, y: 45.0, width: self.view.bounds.size.width, height: self.view.bounds.size.height - 45.0))
textView?.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
textView?.text = "No Results"
textView?.isEditable = false
self.view.addSubview(placesTextField!)
self.view.addSubview(textView!)
}
func textFieldDidChange(placesTextField: UITextField) {
fetcher?.sourceTextHasChanged(placesTextField.text!)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension NewTripAddCitiesViewController: GMSAutocompleteFetcherDelegate {
func didAutocompleteWithPredictions(predictions: [GMSAutocompletePrediction]) {
let resultsStr = NSMutableAttributedString()
for prediction in predictions {
resultsStr.append(prediction.attributedPrimaryText)
resultsStr.append(AttributedString(string: "\n"))
}
textView?.attributedText = resultsStr
}
func didFailAutocompleteWithError(_ error: NSError) {
textView?.text = error.localizedDescription
}
}
func didFailAutocompleteWithError(_ error: NSError) {
has changes to
public func didFailAutocompleteWithError(_ error: Error) {
Related
I am trying to make NSViewControler work with NSView. I can't get myView to call any function in myView(NSView).
myView?.myFunctionName does not work and for NSView I get a nil error when I try set backgoundColor (myview?.layer?.backgroundColor = .white)
class ViewController: NSViewController {
#IBOutlet weak public var myview: NSView!
override func viewDidLoad() {
super.viewDidLoad()
myview?.wantsLayer = true
myview?.layer?.backgroundColor = .white
myview?.needsDisplay = true
}
override var representedObject: Any? {
didSet {
//Update the view, if already loaded.
}
}
}
import Cocoa
class myView: NSView {
var backgroundColor: NSColor?
override func draw(_ dirtyRect: NSRect) {
print("draw")
if let bg = backgroundColor {
bg.setFill()
dirtyRect.fill()
} else {
super.draw(dirtyRect)
print("error:\(String(describing: backgroundColor))")
self.changeBackgroundColor(color: .blue)
}
}
func changeBackgroundColor(color: NSColor) {
// print("change background\( bounds.size)")
// backgroundColor = color
var path = NSBezierPath()
path = NSBezierPath(rect: NSRect(origin:CGPoint(x: 0, y: 0), size: bounds.size))
// print("\(String(describing: path))")
path.close()
color.setFill()
path.fill()
setNeedsDisplay(self.bounds)
}
}
I'm trying to add a gradient to a view but it doesn't work. This is the code:
class TermsAndConditionViewController: UIViewController, NavigationBarToggling {
#IBOutlet weak var tncContentView: UIView!
#IBOutlet weak var tncTextView: UITextView!
#IBOutlet weak var declineButton: AeonButton!
#IBOutlet weak var acceptButton: AeonButton!
#IBOutlet weak var unreadIndicatorView: UIView!
#IBOutlet weak var unreadLabel: UILabel!
let isNavigationBarHidden: Bool = false
let disposeBag = DisposeBag()
lazy var segueManager: SegueManager = {
return SegueManager(viewController: self)
}()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
segueManager.prepare(for: segue)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupViews()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
title = "Terms & Conditions"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupGradientUnreadIndicator()
}
private func setupViews() {
tncContentView.addCorner(radius: 8)
tncContentView.addBorder(width: 1, color: UIColor.black.withAlphaComponent(0.1).cgColor)
}
private func setupGradientUnreadIndicator() {
let gradient = CAGradientLayer()
gradient.type = .axial
gradient.colors = [
R.color.vcSubBgColor()!.withAlphaComponent(0),
UIColor.white
]
gradient.locations = [0, 1]
gradient.startPoint = CGPoint(x: 1, y: 0)
gradient.startPoint = CGPoint(x: 1, y: 1)
gradient.frame = unreadIndicatorView.bounds
unreadIndicatorView.layer.insertSublayer(gradient, at: 0)
}
}
The result is this:
As you can see, the view at the bottom of the Text View is still solid. I was expecting it will go from transparent to white. Can you help me?
Thanks.
PS: BTW, the reason I call the setupGradientUnreadIndicator() in viewDidAppear() is because my whole scene is using auto-layout. So I put it there when the frame of the view is already calculated.
As mentioned by #Larme in the comment, it is because I used UIColor instead of CGColor in the gradient.colors. After I changed it to CGColor it worked fine now.
I want to implement a popup notification into my app when data was being updated successfully or not. To do that I:
created a .xib file: Screenshot
created a class where I load that NIB:
import UIKit
class PopupView: UIView {
static let instance = PopupView()
#IBOutlet weak var backgroundView: UIView!
#IBOutlet weak var popupView: UIVisualEffectView!
#IBOutlet weak var symbol: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
Bundle.main.loadNibNamed("PopupView", owner: self)
popupView.layer.cornerRadius = 20
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func showPopup(title: String, message: String, symbol: UIImage, on viewController: UIViewController) {
self.titleLabel.text = title
self.descriptionLabel.text = message
self.symbol.image = symbol
guard let targetView = viewController.view else { return }
backgroundView.frame = targetView.bounds
targetView.addSubview(backgroundView)
}
In the above class I created a showPopup method where defined a backgroundView frame to be equal to ViewController bounds.
When I call that method in desired ViewController I receive the behaviour where my popupView shows itself and then went off the screen straight away (black area in the GIF): GIF
Would you be so kind to help me understand and fix the reason why the popupView went off the screen and not just equal to a ViewController bounds.
The Code after Shawn Frank's answer
PopupView class:
import UIKit
class PopupView: UIView {
#IBOutlet weak var popupView: UIVisualEffectView!
#IBOutlet weak var symbol: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func configure() {
if let views = Bundle.main.loadNibNamed("PopupView", owner: self) {
guard let view = views.first as? UIView else { return }
view.frame = bounds
addSubview(view)
}
}
func showPopup(title: String, message: String, symbol: UIImage, on viewController: UIViewController) {
titleLabel.text = title
descriptionLabel.text = message
self.symbol.image = symbol
popupView.layer.cornerRadius = 20
popupView.clipsToBounds = true
viewController.view.addSubview(self)
UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveLinear) {
self.popupView.center.y += 40
} completion: { _ in
UIView.animate(withDuration: 0.3, delay: 3.0, options: .curveLinear) {
self.popupView.center.y -= 80
} completion: { _ in
self.popupView.removeFromSuperview()
}
}
}
}
Call in desired ViewController:
let width = CGFloat(10)
let midX = (self.view.frame.width - width) / 2.0
let frame = CGRect(x: midX, y: 0, width: width, height: 135)
let popup = PopupView(frame: frame)
popup.showPopup(title: "Test", message: "Tested super test", symbol: UIImage(named: "checkmark.circle.fill")!, on: self)
Constraints in xib: Screenshot
Current result: GIF
I do not use XIB and storyboard too much these days so I also had to refresh my memory and I used this tutorial
This is the custom PopupView class, I prefer not to use singleton for this but it is my personal preference
class PopupView: UIView {
private let xibName = "PopupView"
#IBOutlet weak var visualEffectBackground: UIVisualEffectView!
#IBOutlet weak var titleLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func configure() {
if let views = Bundle.main.loadNibNamed(xibName, owner: self),
let popupView = views.first as? UIView {
popupView.frame = bounds
addSubview(popupView)
}
}
func showPopup(title: String,
on viewController: UIViewController) {
guard let targetView = viewController.view else { return }
titleLabel.text = title
layer.cornerRadius = 20
clipsToBounds = true
targetView.addSubview(self)
}
}
XIB is set up like this:
Then in the view controller:
#IBAction func didTapPopUp(_ sender: Any) {
// Give your own width, height etc
let width = CGFloat(180)
let midX = (view.frame.width - width) / 2.0
let frame = CGRect(x: midX, y: 100, width: width, height: 80)
let popup = PopupView(frame: frame)
popup.showPopup(title: "Hello", on: self)
}
Gives me this result:
Update based on artexhibit (OPs) comments
The custom view can get its frames in 3 ways that I can think of:
Directly from XIB
By providing frame during programmatic initialization
From the frame set when creating the view in storyboard
To make the view work for all these scenarios, we should not do any frame adjustment inside the custom view and leave it to the parent / container / superview
So I made the following changes to work for all scenarios:
class PopupView: UIView {
private static let xibName = "PopupView"
#IBOutlet weak var visualEffectBackground: UIVisualEffectView!
#IBOutlet weak var titleLabel: UILabel!
init() {
super.init(frame: .zero)
configure()
}
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
private func initializeWithNib() {
var popupView = UIView()
if let views = Bundle.main.loadNibNamed(PopupView.xibName,
owner: self),
let view = views.first as? UIView {
popupView = view
}
frame = popupView.bounds
addSubview(popupView)
}
private func initializeWithFrame() {
if let views = Bundle.main.loadNibNamed(PopupView.xibName,
owner: self),
let popupView = views.first as? UIView {
popupView.frame = bounds
addSubview(popupView)
}
}
private func configure() {
if frame == .zero {
initializeWithNib()
}
else {
initializeWithFrame()
}
layer.cornerRadius = 20
clipsToBounds = true
}
func showPopup(title: String,
on viewController: UIViewController) {
guard let targetView = viewController.view else { return }
print(frame)
titleLabel.text = title
targetView.addSubview(self)
}
}
Now this has the flexibility to work with all 3 scenarios:
// In code
#IBAction func didTapPopUp(_ sender: Any) {
// Default initializer
let popup = PopupView()
var originX = (view.frame.width - popup.frame.width) / 2.0
popup.frame.origin = CGPoint(x: originX, y: 100)
popup.showPopup(title: "Hello", on: self)
// Frame initializer
let popupWidth: CGFloat = 200
let popupHeight: CGFloat = 100
originX = (view.frame.width - popupWidth) / 2.0
let originY = (view.frame.height - popupHeight) / 2.0
let popupFrame = CGRect(x: originX,
y: originY,
width: popupWidth,
height: popupHeight)
let popupTwo = PopupView(frame: popupFrame)
popupTwo.showPopup(title: "Frames", on: self)
}
From storyboard
This gives the following results
I am now working on a UITextfield. I hope to know how to add four discontinuous bottom border to a UITextfield, and how to make the space between input digits larger to make them fit exactly on the four lines respectively. Moreover, if possible, how to make the line become black (while other lines remain grey) when users are inputing digit on that line? Thank you so much!
Use following subclass of UITextField and create textfield for each digit either in storyboard or programatically.
Note that each textfield has to set a tag, such as
1st Digit: textField1.tag=1
2nd Digit: textField1.tag=2
3rd Digit: textField1.tag=3
4th Digit: textField1.tag=4
class CustomTextField: UITextField {
private let normalStateColor = UIColor.lightGray.cgColor
private let focusStateColor = UIColor.black.cgColor
private let border = CALayer()
private let borderHeight: CGFloat = 4.0
// MARK:- Init
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
setup()
}
override init(frame:CGRect) {
super.init(frame:frame)
setup()
}
override func awakeFromNib() {
super.awakeFromNib()
setup()
}
// MARK:- Overrides
override func layoutSubviews() {
super.layoutSubviews()
let size = self.frame.size
self.border.frame = CGRect(x: 0, y: size.height - borderHeight, width: size.width, height: borderHeight)
}
override func willMove(toSuperview newSuperview: UIView!) {
guard newSuperview != nil else {
NotificationCenter.default.removeObserver(self)
return
}
NotificationCenter.default.addObserver(self, selector: #selector(beginEdit),
name: UITextField.textDidBeginEditingNotification, object: self)
NotificationCenter.default.addObserver(self, selector: #selector(endEdit),
name: UITextField.textDidEndEditingNotification, object: self)
}
#objc func beginEdit() {
border.backgroundColor = self.focusStateColor
}
#objc func endEdit() {
border.backgroundColor = self.normalStateColor
}
private func setup() {
border.backgroundColor = self.normalStateColor
textAlignment = .center
borderStyle = .none
layer.addSublayer(border)
delegate = self
}
}
extension CustomTextField: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField.text!.count < 1 && string.count > 0 {
textField.text = string
textField.superview?.viewWithTag(textField.tag + 1)?.becomeFirstResponder()
return false
} else if textField.text!.count >= 1 && string.count == 0 {
textField.text = ""
textField.superview?.viewWithTag(textField.tag - 1)?.becomeFirstResponder()
return false
}
return true
}
}
That yields
check this..
ViewController.swift
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var txtOne: UITextField!
#IBOutlet weak var txtTwo: UITextField!
#IBOutlet weak var txtThree: UITextField!
#IBOutlet weak var txtFour: UITextField!
#IBOutlet weak var vwFour: UIView!
#IBOutlet weak var vwThree: UIView!
#IBOutlet weak var vwTwo: UIView!
#IBOutlet weak var vwOne: UIView!
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == txtOne {
vwOne.backgroundColor = .black
vwTwo.backgroundColor = .lightGray
vwThree.backgroundColor = .lightGray
vwFour.backgroundColor = .lightGray
} else if textField == txtTwo {
vwTwo.backgroundColor = .black
vwOne.backgroundColor = .lightGray
vwThree.backgroundColor = .lightGray
vwFour.backgroundColor = .lightGray
} else if textField == txtThree {
vwThree.backgroundColor = .black
vwTwo.backgroundColor = .lightGray
vwOne.backgroundColor = .lightGray
vwFour.backgroundColor = .lightGray
} else {
vwFour.backgroundColor = .black
vwTwo.backgroundColor = .lightGray
vwThree.backgroundColor = .lightGray
vwOne.backgroundColor = .lightGray
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
Edit:
I was able to change the code in a way, that the program does not crash anymore. However, there is just one tiny problem left for the code to work:
As you can see in my console output, the coordinates for origin2 are not the same as in origin 1.
origin1: (268.0, 241.0) origin2: (116.0, 323.0) sender view center:
Optional((146.5, 397.0))
So I need to get the imageOrigin1-values from viewDidLoad() and use them im func handlePan(sender:UIPanGestureRecognizer).
I just don't know how to do this...
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var image: UIImageView!
#IBOutlet weak var correctField: UIImageView!
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidLoad() {
super.viewDidLoad()
let imageOrigin1 = image.frame.origin
print("origin1: \(imageOrigin1)")
}
#IBAction func handlePan(sender:UIPanGestureRecognizer) {
let imageOrigin2 = image.frame.origin
let translation = sender.translationInView(self.view)
if let view = sender.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
}
if sender.state == UIGestureRecognizerState.Ended {
if correctField.frame.contains(sender.view!.center){
print("Correct")
sender.setTranslation(CGPointZero, inView: self.view)
} else{
print("origin2: \(imageOrigin2)")
print("sender view center: \(sender.view?.center)")
//sender.view?.center = imageOrigin
}
}
}
}
I solved this problem myself, adding a new class and passing the variables via set and get methods:
CLASS:
import UIKit
class Origin {
var positionOrigin: CGPoint!
func setImageOrigin(Image: UIImageView) {
positionOrigin = Image.center
}
func getImageOrigin() -> CGPoint {
return positionOrigin
}
}
VIEW CONTROLLER:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var correctField: UIImageView!
#IBOutlet weak var image: UIImageView!
var imageOrigin = Origin()
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidLoad() {
super.viewDidLoad()
imageOrigin.setImageOrigin(image)
}
#IBAction func handlePan(sender:UIPanGestureRecognizer) {
let translation = sender.translationInView(self.view)
if let view = sender.view {
view.center = CGPoint(x:view.center.x + translation.x,
y:view.center.y + translation.y)
sender.setTranslation(CGPointZero, inView: self.view)
}
if sender.state == UIGestureRecognizerState.Ended {
if correctField.frame.contains(sender.view!.center){
print("Correct")
} else{
image.center = imageOrigin.getImageOrigin()
}
}
}
}