Binding textfield text to enable button not working - swift

I am new to reactive programming, and Bond framework specifically. I know I may be doing things that are wrong in my basic understanding of this programming technique. This is the situation:
I have a UITextView and an "approve" UIButton.
I want the approve button to be enabled only when the text in the textView is not nil. I have tried adding these lines of code into my viewDidLoad method in my ViewController.
textView.reactive.text.observeNext{(text) in
self.message = text
print(text)
}
textView.reactive.text.map { $0 != nil}.bind(to: approveButtonOutlet.reactive.isEnabled)
The first action works (printing the text is happening successfully on every input change).
The second one does not work, the button is enabled both when the text is not nil and when it is.
Any help appreciated.

You can try like
RAC(self.approveButtonOutlet, enabled) = [self.textView.rac_textSignal map:^id(NSString *text) {
return #(text.length > 0);
}];
I'm not sure how it will be in swift 3 just try like
RAC(self.approveButtonOutlet, enabled) = self.textView.rac_textSignal.map({(text: String) -> void in
return (text.length > 0)
})

I found the issue was that I had a placeholder in my textView, which prevented from the text really being nil. So what eventually I did is this:
textView.reactive.text.map {
if $0 == placeholder {
return false
} else if $0 != nil {
return $0!.characters.count > 0
} else{
return false
}
}.bind(to: approveButtonOutlet.reactive.isEnabled)

Related

How can I UITest the change of the image of a UIButton in Swift?

When I tap a UIButton, the image should change to reflect its new state (e.g. Record -> Pause etc).
In my XCode UITest function, how do I interrogate the buttons current image after the tap to assert that its image has changed correctly to the correct image's .png file?
I did it like this
// Find the button by the image name
// In this example the image's names are "record_image" and "pause_image"
// "_" are replaced by " " in the image name when searching
let recordButton = XCUIApplication().buttons["record image"]
recordButton.tap()
XCTAssertFalse(recordButton.exists) // Record button won't exist anymore since the image has changed
let pauseButton = XCUIApplication().buttons["pause image"]
XCTAssertTrue(pauseButton.exists)
pauseButton.tap()
XCTAssertFalse(pauseButton.exists)
XCTAssertTrue(recordButton.exists)
I did like this, not best way but efficient for me,
every image change I changed accessibility identifier then checked the access.ids
public func setFollowed(_ isFollowed: Bool) {
if isFollowed {
followButton.setImage(UIImage(named: "followed-green-icon"), for: .normal)
followButton.accessibilityIdentifier = "ProfileInfoView_followButton_followed"
}
else {
followButton.setImage(UIImage(named: "follow-blue-icon"), for: .normal)
followButton.accessibilityIdentifier = "ProfileInfoView_followButton_follow"
}
}
sample UI test part:
func getFollowButton(isFollowed: Bool) -> XCUIElement {
if isFollowed == true {
return app.descendants(matching: .button)["ProfileInfoView_followButton_followed"]
} else {
return app.descendants(matching: .button)["ProfileInfoView_followButton_follow"]
}
}
then tested returned element, state changed etc.

how to enable button when button already changed in swift

so i have 3 buttons.
the first one for take a picture/pick image from gallery
the second one for take an autograph and segue it to another view controller
the last one is the next button
i want to disable my next button if the user have not full fill the image and the signature.And enable the button if user already full fill the image and the signature
Probably not the best solution but this should work:
button3.isEnabled = false
var signature: String = "" {
didSet {
button3.isEnabled = (image != nil && signature != "")
}
}
var image: UIImage? {
didSet {
button3.isEnabled = (image != nil && signature != "")
}
}

UITable UITextField not displaying data correctly SWIFT 3.0

My application code is pretty lengthly so I would like to try and get this problem resolved without having to show a bunch of code....but here is what is going on..
I have a tableview with dynamic cells all set up just fine. There is a textfield in the cell. When data is entered into the textfield, it is then sent to a label within that same cell. This happen when a "done" button is pressed. This all works just find until I have about 6 rows added to the table. At that point, when the "done" button is pressed, the data gets all mixed up. Sometimes the data is sent to the wrong cell label and other times it doesn't send at all. This only happens when I get to about 6 rows. I am very confused. The cells are displayed based on a Core Data entity.count...the textfield has the delegate set as itself....I have no clue what could be causing this...only after 6 rows!?
Thank you in advance for your help...like I said, I would like to try and avoid pasting my code here but if I have to provide some I will or I can talk you through what is going on in my code...
EDIT
Here is some of my code in cellForRowAt that deals with the textFields in the cell content
if searchResults.count > 1 {
if showInGrams {
if foods[indexPath.row].units != "g" && foods[indexPath.row].units != "grams" && foods[indexPath.row].units != "Grams" {
if cell.changeServingTextField.text == "" {
cell.labelServing.text = String(format: "%0.2f", (100 / foods[indexPath.row].coefficient) * searchResults[indexPath.row].doubleValue)
cell.labelUOM.text = "grams"
} else {
if cell.changeServingSegmentedControl.selectedSegmentIndex == 0 {
cell.labelServing.text = String(format: "%0.2f", Double(cell.changeServingTextField.text!)!)
cell.labelUOM.text = cell.changeServingSegmentedControl.titleForSegment(at: 0)
//show default values
} else {
cell.labelServing.text = String(format: "%0.2f", Double(cell.changeServingTextField.text!)!)
cell.labelUOM.text = cell.changeServingSegmentedControl.titleForSegment(at: 1)
}
}
} else {
if cell.changeServingTextField.text == "" {
cell.labelServing.text = String(format: "%0.2f", searchResults[indexPath.row].doubleValue * foods[indexPath.row].serving)
cell.labelUOM.text = "grams"
}
}
} else {
if cell.changeServingTextField.text == "" {
cell.labelServing.text = String(format: "%0.2f", searchResults[indexPath.row].doubleValue * foods[indexPath.row].serving)
cell.labelUOM.text = foods[indexPath.row].units
} else {
if cell.changeServingSegmentedControl.selectedSegmentIndex == 0 {
cell.labelServing.text = String(format: "%0.2f", Double(cell.changeServingTextField.text!)!)
cell.labelUOM.text = cell.changeServingSegmentedControl.titleForSegment(at: 0)
} else {
cell.labelServing.text = String(format: "%0.2f", Double(cell.changeServingTextField.text!)!)
cell.labelUOM.text = cell.changeServingSegmentedControl.titleForSegment(at: 1)
}
}
}
I hope this helps, thank you
What you are seeing is a symptom of UITableView's reuse strategy. May be what you want to do is store the information entered in your UITextField in a collection and then every time the tableView "reloads", you pick it out from that collection for the current indexPath and load it.
A line like this is never going to work:
if cell.changeServingTextField.text == ""
The problem is that cells are reused. Thus, in its previous life, if the text field had text, it still has text and the test will fail.
Instead, your logic needs to be based entirely on what row this is and what the data corresponding to that row is. And it needs to cover every case, because the cell can be reused.
Things would be clearer if you moved most of this logic into the cells themselves. You can start off by making all your cell's IBOutlets fileprivate and assigning a model object or dictionary as a property of the cell. That way you're separating the logic for creating the cells from their display - and will prevent you using cell's outlets to determine state.
While this might not solve your problem directly, separating the logic might clarify where to look.

UICollectionView Custom layout reloading issue in Swift

This is refrence link:
https://www.raywenderlich.com/107439/uicollectionview-custom-layout-tutorial-pinterest
I have implemented load more in UICollectionView at last cell , the data is getting downloaded, after download complete i want to reload collection
collectionView.collectionViewLayout.invalidateLayout()
self.collectionView.reloadData()
let concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(concurrentQueue) {
DataManager.sharedInstance.homeRequest(Dictionary: params, onSuccess: { (dict) in
self.downloadPageIndex += 1
let response = HomeObject(dictionary: dict)
for (_,e) in (response.dataDict?.enumerate())!{
self.dataArray.append(e)
}
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
self.collectionView.collectionViewLayout.invalidateLayout()
}
self.activityIndicatorCell.stopAnimating()
}, onError: { (error) in
self.activityIndicatorCell.stopAnimating()
})
like this collectionview reloadata
Can any one help me out?
Ran into your question and am currently facing the same problem, after some digging i figured it out!
The problem is within the PintrestLayout file
Inside you will find code like (PROBLEM) :
guard cache.isEmpty == true, let collectionView = collectionView else {
return
}
Basically, if the attributes that you are applying to the CollectionView are empty (they are at the start), apply the PintrestLayout to it, and thats perfect! BUT If you are reloading the data and adding to the collectionView, you need to apply the layout again, but the cache isnt empty when you try to apply the layout to the collectionView again, thus it returns and doesn't work at all...
SOLUTION :
replace that code with :
guard cache.isEmpty == true || cache.isEmpty == false, let collectionView = collectionView else {
return
}
Now you are saying "i dont care if its empty or not, just apply the damn layout to my collectionView"
And Done! Now reloadData() will update how many cells you got...
This is pretty expensive though, if you wanna make it faster, and trim some memory etc... Then look into invalidateFlowLayout Documentation.

Xcode 7 UI Testing - can't type text into search bar

The following UI Test code will successfully tap the UISearchBar element. The software keyboard appears and the search bar looks like it has focus. (ie. it animates as if someone tapped it)
let searchBar = XCUIApplication().otherElements["accessibility_label"]
searchBar.tap()
searchBar.typeText("search text")
However, typeText fails with:
UI Testing Failure - Neither element nor any descendant has keyboard
focus. Element:
Note: Hardware->Keyboard->Connect Hardware Keyboard is toggled off. This solved the same issue for text fields but the search bar is still failing.
I found something:
let app = XCUIApplication()
app.tables.searchFields["Search"].tap()
app.searchFields["Search"].typeText("something")
It looks strange, but it works for me.
it worked for me to set
searchBar.accessibilityTraits = UIAccessibilityTraitSearchField
in the view controller and then access it in the UI test by
let searchfield = app.searchFields.element(boundBy: 0)
searchfield.typeText("foobar")
I used the following workaround successfully:
let firstCell = app.cells.elementBoundByIndex(0)
let start = firstCell.coordinateWithNormalizedOffset(CGVectorMake(0, 0))
let finish = firstCell.coordinateWithNormalizedOffset(CGVectorMake(0, 3))
start.pressForDuration(0, thenDragToCoordinate: finish)
app.searchFields.element.tap()
app.searchFields.element.typeText("search text")
This effectively scrolls the search bar into view and taps it to focus, after that typeText() can be used.
I use the following (Swift 4, Xcode 9.4.1):
app.searchFields["Placeholder"].clearAndEnterText("String to search")
app.buttons["Search"].tap()
... where "Placeholder" is the string that appears in the textfield before typing the text.
Note: clearAndEnterText is a function created in a XCUIElement extension:
func clearAndEnterText(_ text: String)
{
guard let stringValue = self.value as? String else {
XCTFail("Tried to clear and enter text into a non string value")
return
}
self.tap()
let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: stringValue.count)
self.typeText(deleteString)
self.typeText(text)
}
Hope this can help you.
Just put a wait for few seconds and it will work!
let searchBar = XCUIApplication().otherElements["accessibility_label"]
searchBar.tap()
sleep(5)
searchBar.typeText("search text")