Swift Change statusBar backgoundColor and textColor - background-color

ANSWER:
I/ Open Info.plist file -> Add new Row: View controller-based status bar appearance ->Set to [YES]
II/ Open yourViewController.swift file and insert:
override func prefersStatusBarHidden() -> Bool {
// Return false -> Show statusBar
return false
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
// Change status background color
let statusBar: UIView = UIApplication.sharedApplication().valueForKey("statusBar") as! UIView
if statusBar.respondsToSelector(Selector("setBackgroundColor:")) {
statusBar.backgroundColor = UIColor.redColor()
}
// Change textColor -> While(LightContent)
return UIStatusBarStyle.LightContent
}

Related

Set range of colored text for UISegmentedControl

According to the following documentation (https://developer.apple.com/documentation/uikit/uisegmentedcontrol/1618570-settitletextattributes)
I should be able to add attributes to change how it looks for a particular mode.
modalitySegmentedControl.setTitle("LDR ("+(stateController?.tdfvariables.selectedRadionuclide.name ?? "-") + ")", forSegmentAt: Constants.LDRButton)
let colorAttribute = [ NSAttributedString.Key.foregroundColor: UIColor.systemTeal ]
modalitySegmentedControl.setTitleTextAttributes(colorAttribute, for: .selected)
In short the text on the control is basically "LDR (I-125)". Currently this code highlights the entire selection teal. I'm looking for a way to only highlight the (I-125) only with a teal color. I can do this with regular UILabels by defining a range that the attributes act upon, but I can't seem to find a way to set a specific color range with the UISegmentedControl?
Is this possible to do?
It currently looks like this:
I want the LDR to be white color and only teal on the (I-125) part.
In short I think it's not possible. Check my hacky playground:
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
extension UIView {
class func getAllSubviews<T: UIView>(from parentView: UIView) -> [T] {
return parentView.subviews.flatMap { subView -> [T] in
var result = getAllSubviews(from: subView) as [T]
if let view = subView as? T { result.append(view) }
return result
}
}
class func getAllSubviews(from parentView: UIView, types: [UIView.Type]) -> [UIView] {
return parentView.subviews.flatMap { subView -> [UIView] in
var result = getAllSubviews(from: subView) as [UIView]
for type in types {
if subView.classForCoder == type {
result.append(subView)
return result
}
}
return result
}
}
func getAllSubviews<T: UIView>() -> [T] { return UIView.getAllSubviews(from: self) as [T] }
func get<T: UIView>(all type: T.Type) -> [T] { return UIView.getAllSubviews(from: self) as [T] }
func get(all types: [UIView.Type]) -> [UIView] { return UIView.getAllSubviews(from: self, types: types) }
}
class MyViewController : UIViewController {
var myString: String = "LDR (I-125)"
var myString42: String = "424242424242"
var attributedString = NSMutableAttributedString()
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let items = ["EBRT", "LDR (I-125)", "PERM"]
let modalitySegmentedControl = UISegmentedControl(items: items)
modalitySegmentedControl.frame = CGRect(x: 20, y: 200, width: 300, height: 20)
modalitySegmentedControl.backgroundColor = .white
attributedString = NSMutableAttributedString(string: myString, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 18)])
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.red, range: NSRange(location:4, length:7))
let subviews = modalitySegmentedControl.getAllSubviews()
for view in subviews {
if view is UILabel {
if let label = view as? UILabel, label.text == myString {
print(label.attributedText)
label.attributedText = attributedString
//label.text = "42" // this works
print(label.attributedText) // looks changed
}
}
}
let subviews2 = modalitySegmentedControl.getAllSubviews()
for view in subviews2 {
if view is UILabel {
if let label = view as? UILabel, label.text == myString {
print(label.attributedText) // but it didn't change
}
}
}
let lab = UILabel()
lab.frame = CGRect(x: 40, y: 250, width: 300, height: 20)
lab.attributedText = attributedString
view.addSubview(lab)
view.addSubview(modalitySegmentedControl)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
You can find specific UILabel subview of UISegmentedControl and even can change the text, but attribute changes doesn't work.
Related question: Segmented Control set attributed title in each segment

SwiftUI UITextView wrapper how to size it content to match parent

I have such UITextView wrapper and it works but when I place it inside List row. I would like it to autosize, i.e. have width that match parent in this case List Row VStack { }, and height that is autosize based on text length. Text should wrap.
Now it nearly works but the longer text like a URL goes outside available row width.
List {
VStack(alignment: .leading) {
if self.hasNote {
TextView(text: text)
}
}
struct TextView: UIViewRepresentable {
// MARK: - Properties
let text: String
init(text: String) {
self.text = text
}
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
//textView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
textView.backgroundColor = UIColor.clear
textView.isScrollEnabled = false
textView.attributedText = self.attributedText
textView.textContainer.lineBreakMode = .byWordWrapping
//textView.layoutIfNeeded()
textView.sizeToFit()
return textView
}
}
UPDATE
Now I have something like this it does fit parent SwiftUI views (rows in List) but instead it does not wrap text (or stretch vertically based on content size. If scrolled it wraps text correctly.
Also if I set .frame(height: 500) I can see it wraps text. But it doesn't autosize correctly.
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.backgroundColor = UIColor.clear
textView.isScrollEnabled = false
textView.isSelectable = true
let linkAttrs : [NSAttributedString.Key : Any] = [
.foregroundColor: accentColor,
.underlineColor: accentColor,
.underlineStyle: NSUnderlineStyle.single.rawValue
]
textView.linkTextAttributes = linkAttrs
textView.attributedText = self.attributedText
textView.textContainer.lineBreakMode = .byWordWrapping
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
textView.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
textView.setContentHuggingPriority(.defaultHigh, for: .horizontal)
textView.setContentHuggingPriority(.defaultLow, for: .vertical)
textView.sizeToFit()
return textView
}

Pan to new mapView Annotation Location - not zoom

When adding an annotation to my mapView using mapView.addAnnotation, mapView.setRegion correctly animates and displays the map's center & span.
But when adding a new annotation and then calling mapView.setRegion, the view again starts from very wide, and then animates/zooms in to the new center & span.
I would like to know if there is a way to PAN from the previous region to the new region (center & span), rather than starting zoomed out, then zooming all the way back in again.
mapView.setRegion starts zoom from way out here each time:
I have defined 2 custom classes : MarkAnnotation & MarkAnnotationView
class MarkAnnotation : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) {
self.coordinate = coordinate
self.title = title
self.subtitle = subtitle
}
}
class MarkAnnotationView: MKAnnotationView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
guard let markAnnotation = self.annotation as? MarkAnnotation else { return }
image = UIImage(named: "customAnnotation")
}
}
and in my MKMapViewDelegate :
func addMarkAnnotation() {
for mark in marks {
let markName : String = mark.name
let markLat : Double = mark.lat
let markLon : Double = mark.lon
let markLL = CLLocationCoordinate2DMake(markLat, markLon)
let markAnnotation = MarkAnnotation(coordinate:markLL, title:markNumStr, subtitle: markName)
mapView.addAnnotation(markAnnotation)
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MarkAnnotationView(annotation: annotation, reuseIdentifier: "MarkView")
annotationView.canShowCallout = true
return annotationView
}
func centerMapOnLocation(location:CLLocation, span:Double) {
let mapSpan = MKCoordinateSpanMake(0.2, 0.2)
let recenterRegion = MKCoordinateRegion(center: location.coordinate, span: mapSpan)
mapView.setRegion(recenterRegion, animated: true)
}
And then in the ViewController, when user presses a button, a mark is added at a specified lat/lon
func addMarkBtnPressed() {
// .... (have not shown : conversion of text fields from String to Double, adding Mark to marks array, etc ) ... All this works fine
addMarkAnnotation()
let mapCenter = CLLocation(latitude: markLat, longitude: markLon)
centerMapOnLocation(location: mapCenter, span: span)
}
Try the following code:
func centerMapOnLocation(location:CLLocation, span:Double) {
let mapSpan = mapView.region.span
let recenterRegion = MKCoordinateRegion(center: location.coordinate, span: mapSpan)
mapView.setRegion(recenterRegion, animated: true)
}

Swift 4 - How do I test for TextView content did change?

Swift 4, iOS 11 - I have a UITextView that is pre-populated with text but I want users to be able to save any changes they make to the content there. I also have a Save button in the navigation bar and I would like to disable it until the user actually changes the text in the TextView.
I know how to test for empty but I don't know how to test for when the text has been edited. How do I modify the following to test for changes to the content of TextView?
#IBAction func textEditingChanged(_ sender: UITextView) {
updateSaveButtonState()
}
func updateSaveButtonState() {
let descriptionText = descriptionTextView.text ?? ""
saveButton.isEnabled = !descriptionText.isEmpty
}
We'll to use it a dynamic way and not only in single place, i tried to make it easier to implement around the whole app, subclassing the UITextView is one of the only ways we got here #holex has suggested isEdited boolean flag and it gave me an idea, Thanks to that.
Here is the steps to implement it:
First of all set the defaultText of the textView and set the target of the method that will be called when the textView will be edited, so you can customize what ever you want.
#IBOutlet weak var saveButton: UIBarButtonItem!
#IBOutlet weak var textView: SBTextView!{
didSet{
textView.defaultText = "Hello"
textView.setTarget = (selector:#selector(self.updateSaveButtonState),target:self)
}
}
Lets say you'll setup the saveButton in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// setup save button action
saveButton.action = #selector(saveAction(_:))
saveButton.target = self
self.updateSaveButtonState()
}
And last is your save action and the selector to update the view using isEdited flag.
//MARK:- Actions
#objc private func updateSaveButtonState(){
// has not been changed keep save button disabled
if self.textView.isEdited == false{
self.saveButton.isEnabled = false
self.saveButton.tintColor = .gray
}else {
// text has been changed enable save button
self.saveButton.isEnabled = true
self.saveButton.tintColor = nil // will reset the color to default
}
}
#objc private func saveAction(_ saveButton:UIBarButtonItem){
self.textView.updateDefaultText()
}
TextView Custom Class:
//
// SBTextView.swift
//
//
// Created by Saad Albasha on 11/17/17.
// Copyright © 2017 AaoIi. All rights reserved.
//
import UIKit
class SBTextView: UITextView,UITextViewDelegate {
var isEdited = false
private var selector : Selector?
private var target : UIViewController?
var setTarget: (selector:Selector?,target:UIViewController?) {
get{
return (selector,target)
}
set(newVal) {
selector = newVal.0
target = newVal.1
}
}
var textViewDefaultText = ""
var defaultText: String {
get {
return textViewDefaultText
}
set(newVal) {
textViewDefaultText = newVal
self.text = newVal
self.isEdited = false
}
}
//MARK:- Life Cycle
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
self.setupTextview()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupTextview()
}
private func setupTextview(){
// setup textview
self.text = textViewDefaultText
self.delegate = self
}
func updateDefaultText(){
self.defaultText = self.text!
// update save button state
target!.perform(self.selector, with: nil, with: nil)
}
//MARK:- Delegate
internal func textViewDidChange(_ textView: UITextView) {
if textViewDefaultText != textView.text! {
isEdited = true
}else {
isEdited = false
}
// update save button state
target!.perform(self.selector, with: nil, with: nil)
}
}
I hope this helps.

How do I detect Tap and Hold on a pin in MKMapView?

The title says it all. I'm trying to detect a tap on a pin in a MKMapView and I don't even know where to begin. Its not an UIView, so I can't add a gesture recognizer and I can't find a UIView in MKPlaceMark to add it.
You're question is not very clear but you can do like this:
import UIKit
import MapKit
protocol HandleMapSearch: class {
func dropPinZoomIn(placemark:MKPlacemark)
}
class ViewController: UIViewController {
func getDirections(){
// Here you can put anythings like:
guard let selectedPin = selectedPin else { return }
let mapItem = MKMapItem(placemark: selectedPin)
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
mapItem.openInMapsWithLaunchOptions(launchOptions)
}
}
extension ViewController : MKMapViewDelegate {
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView?{
guard !(annotation is MKUserLocation) else { return nil }
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
}
pinView?.pinTintColor = UIColor.orangeColor() // The pin's color
pinView?.canShowCallout = true // To set dialogue bubbles of the pin.
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: CGPointZero, size: smallSquare)) // To initialize the button in the dialogue bubbles of the pin.
button.addTarget(self, action: #selector(ViewController.getDirections), forControlEvents: .TouchUpInside) // To set and initialize the button.
pinView?.leftCalloutAccessoryView = button
return pinView
}
}
You can have more details in Thorn web site

Resources