I meet a question. I am using following code to display UIActivityIndicatorView. My requirement is to be able to create an UIActivityIndicatorView and display it when I click the button with tag 1, if I click other buttons the UIActivityIndicatorView will be removed from the super view.
class ViewController: UIViewController {
lazy var acLoad:(UIActivityIndicatorView) = {
let myActivityIndicator = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.white)
myActivityIndicator.center = view.center
myActivityIndicator.hidesWhenStopped = true
myActivityIndicator.startAnimating()
return myActivityIndicator
}()
//more code
#objc func btnAction(sender: UIButton){
switch sender.tag {
case 1:
print("created")
view.addSubview(acLoad)
acLoad.startAnimating()
default:
print("removed")
acLoad.stopAnimating()
acLoad.removeFromSuperview()
}
}
}
The above doesn't work, I can get the print log after click, but UIActivityIndicatorView doesn't display, any ideas?
It seems like your UIActivityIndicatorView might be misplaced (wrong frame value) - so it is not visible to you.
print("created")
view.addSubview(acLoad)
acLoad.startAnimating()
// Add this line in your button tap code
// It will tell you where it is placed inside your view
print(acLoad.frame)
If you find that it's frame is not where you want it to be, just fix your layout code and it will be where you expect it to be.
Another note - myActivityIndicator.hidesWhenStopped = true makes it automatically hide on stopAnimating() call. So you can add it only once, not remove-add every time.
Also check your view.backgroundColor vs acLoad style.
Related
I am making an app where a user can click anywhere on the window and a NSTextView is added at the mouse location. I have got it working with the below code but I am not able to make it active (in focus) after adding it to the view (parent view). I have to click on the NSTextView to make it active but this is not what I want. I want it to automatically become active when its added to the parent view.
Code in my ViewController to add the NSTextView to its view:
private func addText(at point: NSPoint) {
let textView = MyTextView(frame: NSRect(origin: point, size: CGSize(width: 150.0, height: 40.0)))
view.addSubview(textView)
}
MyTextView class looks like below:
class MyTextView: NSTextView {
override var shouldDrawInsertionPoint: Bool {
true
}
override var canBecomeKeyView: Bool {
true
}
override func viewWillDraw() {
isHorizontallyResizable = true
isVerticallyResizable = true
insertionPointColor = .red
drawsBackground = false
isRichText = false
allowsUndo = true
font = NSFont.systemFont(ofSize: 40.0)
}
}
Also, I want it to lose focus (become inactive) when some other elements (view) are clicked. Right now, once a NSTextView becomes active, it stays active no matter what other elements I click except when I click on an empty space to create yet another NSTextView.
I have gone through the Apple docs multiple times but I think I am missing something. Any help would be much appreciated.
Get the NSWindow instance of the NSViewController's view and call makeFirstResponder passing the text view as parameter.
To lose focus call makeFirstResponder passing nil.
I have got a label that appears in the NavigationBar when the View of the FirstViewController opens for the first time. The label shows a number that should be able to change. If I click a button on the FirstViewController the View of the SecondViewController shows up in which's NavigationBar the label of the FirstViewController is still visible. When I change the number that is written in the label by clicking on a button in the SecondViewController the number of the label only changes when I go back to the View of the FirstViewController. That is because I update the title in the ViewDidLoad String loop.
Now my question is:
I want the number of the label to be changed at the moment when I click the button in the SecondViewController, although the label was defined in the Code of the FirstViewController. The number of the label shows an amount of money.
This is the Code of the FirstViewController:
var moneyLabel: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationLabel()
let newMoney:String = String(format: "%f", money)
updateTitle(title: "\(newMoney)")
}
func updateTitle(title: String) {
if let myTitleView = self.moneyLabel {
myTitleView.text = title
}
}
func setupNavigationLabel() {
let navigationBar = self.navigationController?.navigationBar
let moneyFrame = CGRect(x: 300, y: 0, width:
(navigationBar?.frame.width)!/2, height: (navigationBar?.frame.height)!)
moneyLabel = UILabel(frame: moneyFrame)
moneyLabel?.text = "\(money)"
navigationBar?.addSubview(moneyLabel!)
}
It seems that your problem is with the second ViewController but you are not sharing any code!!! Use the below to achieve your objective.
class secondViewControler:UIViewController{
var money:Int
var moneyLabel:UILAbel?
override func viewDidLoad(){
self.money= //pass the amount, i dont know which way you use to store and retrieve the amount
moneyLabel.text="\(money)"
}
#IBAction updateMoney(){
//get the amount from the textfield say the output is X
money=X
moneyLabel.text="\(money)"
}
}
My app has quite a few buttons on each screen as well as a UIBarButtonItem back button and I have problems with people being able to multi click buttons. I need only 1 button to be clickable at a time.
Does anyone know how to make a UIBarButtonItem back button exclusive to touch?
I've managed to disable multi clicking the UIButtons by setting each one's view to isExclusiveTouch = true but this doesn't seem to count for the back button in the navigation bar.
The back button doesn't seem to adhere to isExclusiveTouch.
Does anyone have a simple work around that doesn't involve coding each and every buttons send events?
Many Thanks,
Krivvenz.
you can enable exclusive touch simply this will stop multiple touch until first touch is not done
buttton.exclusiveTouch = true
You could write an extension for UIBarButtonItem to add isExclusiveTouch?
you can simply disable the multi-touch property of the super view. You can also find this property in the storyboard.
you could try this for the scene where you want to disable multiple touch.
let skView = self.view as! SKView
skView.isMultipleTouchEnabled = false
I have found a solution to this. isExclusiveTouch is the solution in the end, but the reason why this property didn't do anything is because you need to set it also on all of the subviews of each button that you want to set as isExclusiveTouch = true. Then it works as expected :)
It's working fine in Swift
self.view.isMultipleTouchEnabled = false
buttonHistory.isExclusiveTouch = true
In addition of #Luky LĂzal answer, you need pay attention that all subviews of a view you want disable multi-touching (exactly all in hierarchy, actually subviews of subviews) must be set as isExclusiveTouch = true.
You can run through all of them recursively like that:
extension UIView
{
func allSubViews() -> [UIView] {
var all: [UIView] = []
func getSubview(view: UIView) {
all.append(view)
guard view.subviews.count > 0 else { return }
view.subviews.forEach{ getSubview(view: $0) }
}
getSubview(view: self)
return all
}
}
// Call this method when all views in your parent view were set
func disableMultipleTouching() {
self.isMultipleTouchEnabled = false
self.allSubViews().forEach { $0.isExclusiveTouch = true }
}
I have a UIViewController with a UILabel that needs to display either "lbs" or "kg". My app has a settings screen (another UIViewController) that is presented modally over the first view controller and the user can select either of the two units and save their preference. If the units are changed and the modal settings screen is dismissed, I of course want the label on the first view controller to be updated with the new units value (but without refreshing the whole view). I thought I knew how to make it work, but evidently I don't.
On my modal settings screen, I have a UISegmentedControl to allow the user to select units. Anytime it's changed, this function updates userDefaults:
func saveUnitsSelection() {
if unitsControl.selectedSegmentIndex == 0 {
UserDefaultsManager.sharedInstance.preferredUnits = Units.pounds.rawValue
} else {
UserDefaultsManager.sharedInstance.preferredUnits = Units.kilograms.rawValue
}
}
Then they would likely dismiss the settings screen. So, I added this to viewDidLoad in my first view controller:
override func viewDidLoad() {
super.viewDidLoad()
let preferredUnits = UserDefaultsManager.sharedInstance.preferredUnits
units.text = preferredUnits
}
That didn't work, so I moved it to viewWillAppear() and that didn't work either. I did some research and some caveman debugging and found out that neither of those functions is called after the view has been loaded/presented the first time. It seems that viewWillAppear will be called a second time if I'm working within a hierarchy of UITableViewControllers managed by a UINavigationController, but isn't called when I dismiss my modal UIViewController to reveal the UIViewController underneath it.
Edit 1:
Here's the view hierarchy I'm working with:
I'm kinda stuck at this point and not sure what to try next.
Edit 2:
The user can tap a 'Done' button in the navigation bar and when they do, the dismissSettings() function dismisses the Settings view:
class SettingsViewController: UITableViewController {
let preferredUnits = UserDefaultsManager.sharedInstance.preferredUnits
// some other variables set here
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.topItem?.title = "Settings"
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: .Plain, target: self, action: #selector(self.dismissSettings(_:)))
if preferredUnits == Units.pounds.rawValue {
unitsControl.selectedSegmentIndex = 0
} else {
unitsControl.selectedSegmentIndex = 1
}
}
func dismissSettings(sender: AnyObject?) {
navigationController?.dismissViewControllerAnimated(true, completion: nil)
}
}
THE REAL PROBLEM
You misspelled viewWillAppear. You called it:
func viewWillAppear()
As far as Cocoa Touch is concerned, this is a random irrelevant function that hooks into nothing. You meant:
override func viewWillAppear(animated: Bool)
The full name of the first function is: "viewWillAppear"
The full name of the second function is: "viewWillAppear:animated"
Once you get used to this, the extreme method "overloading" that Cocoa Touch uses gets easier.
This is very different in other languages where you might at least get a warning.
The other lesson that everyone needs to learn when posting a question is: Include All Related Code!
Useful logging function I use instead of print or NSLog, to help find these things:
class Util {
static func log(message: String, sourceAbsolutePath: String = #file, line: Int = #line, function: String = #function, category: String = "General") {
let threadType = NSThread.currentThread().isMainThread ? "main" : "other"
let baseName = (NSURL(fileURLWithPath: sourceAbsolutePath).lastPathComponent! as NSString).stringByDeletingPathExtension ?? "UNKNOWN_FILE"
print("\(NSDate()) \(threadType) \(baseName) \(function)[\(line)]: \(message)")
}
}
[Remaining previous discussion removed as it was incorrect guesses]
here is the screenshot
when user get into this page, I want to change the background of those button.
for example, when the level of user is 6, I want to change the top 6 background of buttons to a blue picture as first button. I don't wanna write a code one by one, so please help me to write a function, Thank you very much!
You can add you buttons to the array in order of increasing level and do something like this:
var buttons: [UIButton] // array of your buttons, next you need to init it
...
func setColorForUserLevel(userLevel: Int) {
if userLevel > buttons.count {
return
} else {
for i in 0...userLevel - 1 {
buttons[i].backgroundColor = UIColor.blueColor()
}
}
}
I create simple example project to show how to use it:
I have outlets with UIButton! type and buttons object in code:
And i test setColorForUserLevel function in viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
buttons = [button1, button2, button3]
setColorForUserLevel(2)
}