Vary User Defined Runtime Attributes for Traits - ios

I have created a view using Storyboard which has rounded corners. However I'd like to remove this for iPhone. I'm using user defined runtime attributes to get this done and I wonder if I can make a variation for iPhone.
Edit: I know I could do this in code, but it sounds silly since it's something visual and the storyboard file sounds like the right place to do this variation.

extension CALayer {
var borderUIColor: UIColor {
set {
if UIDevice.current.model == "iPhone" {
self.borderColor = newValue.cgColor
}
// do whatever you want
}
get {
return UIColor(cgColor: self.borderColor!)
}
}
}
then use layer.borderUIColor to access the attribute.
Write an extension and redefine attributes, judge the device type in the set method would fix this problem! hope this fix your problem !

I don't think it's possible with Storyboards. You could just check for device type in code when loading that particular view and override the properties.
E.g.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if UIDevice.current.model == "iPhone" {
// reset attributes
}
}

Related

UIButton returns nil

So this is my situation: I have a class:
class upgrade {
var buttonOutlet: UIButton?
init(buttonOutlet: UIButton?) {
self.buttonOutlet = buttonOutlet }
}
Then, I have one upgrade:
var upgrade1 = upgrade.init(buttonOutlet: nil)`
Obviously, I can't set the 'buttonOutlet' at that point.
So I have a function that sets it for me when needed:
func setBO() {
upgrade1.buttonOutlet? = buttonOutletForUpgrade1
}
This "buttonOutletForUpgrade1" is an outlet from a button I created earlier.
I want to be able to set the button's title after calling 'setBO'. However, it doesn't do anything!
When I run the code, my upgrade1.buttonOutlet returns nil. How can this be? Didn't I set it right? Please explain!
Thanks!
Change your code as follows and it works. Tested before and after and it works.
func setBO() {
upgrade1.buttonOutlet = buttonOutletForUpgrade1
}
Don't add a question mark at the end.

UI Tests - isSelected is always returning false

We have updated out Swift 2.3 project to Swift 3 recently using Xcode 8.2.1 (8C1002), and now most of our UI Tests related with tableViews and the isSelected property aren't working. It's always returning false, even when the object is selected (we can see it in the iOS Simulator).
Has anyone experienced similar issues? Our code used to work normally in Swift 2.3 before the conversion. Here is how we retrieve a tableView cell:
let cell = app.tables.cells.element(at: 4)
Note: app is a XCUIApplication.
And here is how we check if it's selected or not:
XCTAssert(cell.isSelected)
Another observation is that we are sure that the object exists because waitForExpectations is returning true:
let existsPredicate = NSPredicate(format: "exists = 1")
expectation(for: existsPredicate, evaluatedWith: cell, handler: nil)
waitForExpectations(timeout: 20, handler: nil)
EDIT: In order to replace isSelected, I've tried to use NSPredicate with selected = 1 and with isSelected = 1. None worked. I also tried to use acessibilityValue based in other question's answer, however it wasn't that simple since sometimes the items in my table view are selected/unselected programatically. Also, that method involved adding test code to the app, which isn't a good practice.
EDIT AFTER BOUNTY END: Since no one could find a solution for that problem and that's obviously a bug in Xcode, I've submitted a bug report to Apple. I will comment here when they release an Xcode version with the fix.
EXTRA EDIT: One day after my last edit, dzoanb came with a functional answer.
I made a few tests and a little research. You can check out the app created for this purpose >>here<<. It would be great if you could check it out (it required a little bit of work). There are also UI tests to prove it works. Also, two options are available, one is vanilla XCTest and one library with a lot of helpers I'm creating with my colleagues AutoMate. But that's not the point.
Here is what I found out:
1) isSelected property of XCUIElement depends on accessibilityTrait. Element to be selected in XCTest has to have UIAccessibilityTraitSelected set.
2) I couldn't reproduce Your problem but I was able to control isSelected property.
3) Yes, it requires a little bit of code, but should work well with VoiceOver if it is important for You.
All necessary code is in Your custom UITableViewCell subclass. And uses overriding UIAccessibilityElement accessibilityTraits property.
private var traits: UIAccessibilityTraits = UIAccessibilityTraitNone
// MARK: UITableViewCell life cycle
override func awakeFromNib() {
super.awakeFromNib()
traits = super.accessibilityTraits
}
// MARK: UIAccessibilityElement
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isSelected {
return traits | UIAccessibilityTraitSelected
}
return traits
}
set {
traits = newValue
}
}
Hope it helps.
Couldn't get that code to compile under Swift 4.
This worked for me.
public override var accessibilityTraits: UIAccessibilityTraits {
get {
if isSelected {
return super.accessibilityTraits.union(.selected)
}
return super.accessibilityTraits
}
set {
super.accessibilityTraits = newValue
}
}
Have you tried making a break point before and after the tap, and check the value of the cell? Like the WWDC video here: https://youtu.be/7zMGf-0OnoU
(See from 10 minutes in)
isSelected only works on views which inherit from UIControl. UIControl.isSelected informs XCUIElement.isSelected.
Since UITableViewCell does not inherit from UIControl, you aren't seeing the value you want in your tests when you observe cell.isSelected.
I suggest that if you want this to be testable via UI tests that you file a feature request with Apple to make UIControl a protocol, which you could then extend your cells to conform to, or add UITableViewCell.isSelected to the properties that inform XCUIElement.isSelected.
#dzoanb solution can work without adding a private var:
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isSelected {
return super.accessibilityTraits | UIAccessibilityTraitSelected
}
return super.accessibilityTraits
}
set {
super.accessibilityTraits = newValue
}
}

Recognizing subview's class

I decided to animate my objects manually and therefore made an extension for UIView class:
public extension UIView{
func slideOut(){
UIView.animateWithDuration(0.5, animations: { self.frame.origin.x = -self.frame.width }, completion: finishedDisposing)
}
func finishedDisposing(successfully: Bool){
if !successfully{
((UIApplication.sharedApplication().delegate as! AppDelegate).window!.rootViewController as! VC).showSystemMessage("Failed to dispose one or more subviews from superview", ofType: .NOTICE)
}
responder.viewDisposed()
}
}
Which works nice and I have no problems about it, BUT I have a method in VC (Custom UIViewController) viewDisposed() which is called whenever a view slides out of sight and it has such an implementation:
func viewDisposed() {
disposed++
print("Updated disposed: \(disposed) / \(self.view.subviews.count)")
if disposed == self.view.subviews.count - 1{
delegate.vcFinishedDisposing()
}
}
It shows that self.view.subviews contains all my custom views + 3 more (UIView, _UILayoutGuide x 2). They do extend UIView although do not callresponder.viewDisposed method. My decision was to figure out how to get classes of each subview and Mirror(reflecting: subView).subjectType if I print it does it wonderfully. Is there any way to actually compare this to anything or, better, get String representation? Basically, I want you to help me create a method which would create a stack of subviews which are not of type UIView (only subClasses) nor _UILayoutGuide. Thank you!
You'd probably be better off directly creating an array of just the subviews you care about, instead of starting with all subviews and trying to filter out the ones you don't care about. Those layout guides weren't always there—they were added in iOS 7. Who knows what else Apple will add in the future?
Anyway:
let mySubviews = view.subviews.filter {
!["UIView", "_UILayoutGuide"].contains(NSStringFromClass($0.dynamicType))
}

How to disable auto-complete when running Xcode UI Tests?

As part of my UI Tests, I'm generating a random string as titles for my objects. The problem is that when this title is input via a keyboard (using XCUIElement.typeText()), iOS sometimes accepts an auto-suggested value instead.
For example, I may want it to type an auto generated string of "calg", but auto correct will choose "calf" instead. When I try to look for this value later on with an assertion, it doesn't exist and fails incorrectly.
Is there a way to tell the UI tests that they shouldn't be using auto correct, or are there an workarounds I can use?
Unless you need auto-suggest for any test scenarios, did you try turning off auto-correction in device/simulator settings.
Settings-> General -> Keyboard -> Auto-Correction
I don't believe you can turn off auto-correction through code from your UI Testing target.
You can, however, turn it off for the individual text view from your production code. To make sure auto-correction is still on when running and shipping the app, one solution would be to subclass UITextField and switch on an environment variable.
First set up your UI Test to set the launchEnvironment property on XCUIApplication.
class UITests: XCTestCase {
let app = XCUIApplication()
override func setUp() {
super.setUp()
continueAfterFailure = false
app.launchEnvironment = ["AutoCorrection": "Disabled"]
app.launch()
}
func testAutoCorrection() {
app.textFields.element.tap()
// type your text
}
}
Then subclass (and use) UITextField to look for this value in the process's environment dictionary. If it's set, turn auto-correction off. If not, just call through to super.
class TestableTextField: UITextField {
override var autocorrectionType: UITextAutocorrectionType {
get {
if NSProcessInfo.processInfo().environment["AutoCorrection"] == "Disabled" {
return UITextAutocorrectionType.No
} else {
return super.autocorrectionType
}
}
set {
super.autocorrectionType = newValue
}
}
}
Here is how I disabled it in my UI Test
app.textFields.element(boundBy: 0).tap()
let keyboards = app.keyboards.count
XCTAssert(keyboards > 0, "You need enable the keyboard in the simulator.")
app.buttons["Next keyboard"].press(forDuration: 2.1)
let predictiveOn = app.switches["Predictive"].value as! String == "1"
if predictiveOn {
app.switches["Predictive"].tap()
} else {
app.buttons["Next keyboard"].tap()
}
app.buttons["Next keyboard"].press(forDuration: 2.1)
let predictiveOff = app.switches["Predictive"].value as! String == "0"
XCTAssert(predictiveOff, "Predictive mode is not disabled")
app.buttons["Next keyboard"].tap()

Swift2 override of highlighted/selected doesn't work

I'm creating a custom UIButton class and i'm trying to override the highlighted/selected methods but they aren't called. After a bit of searching i found that this code should be working:
override var highlighted: Bool {
didSet {
if highlighted {
self.backgroundColor = UIColor.whiteColor()
} else {
self.backgroundColor = UIColor.blackColor()
}
}
}
I did the same for selected. I also tried using willSet but no luck. I'm using swift2.0. Could that make the difference? Anyone knows why it isn't called?
You're going about this all wrong. No need to subclass. Just call setBackgroundImage:forState: with a black image for one state and a white image for the other.
Issue fixed. Due to the fact that i'm wokrking on an SDK shared library, I had to define the Module of my view controller and the Module of my button class. Once I did those, everything was working fluently.

Resources