I have a small test project that I'm trying to import into my production project. The ViewController that is causing the problem consists of a back ground image and a Scroll View (SV).
3 images appear in the test SV, yet only 2 appear in production. There appears to be a gap where the first image should appear.
Please note. The first image is the background image for the VC. I set it, then delete it from the array that feeds the scrollview.
Here are the two ViewControllers. Please note I embedded the VC in a TabBar and NavBar controller in test because that is what I have int production.
What is most puzzling is the code is exactly the same. The image URL's are the same. But the number of UIImageViews added to the scrollView are different. Note the last print statement in the code:
func setupList() {
print(foodPairings.count)
let imageStringURL = foodPairings[0].imageURL
let encodedURL = imageStringURL.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed)
guard let imageURL: URL = URL(string: encodedURL!) else {return}
bgImage.af_setImage(withURL: imageURL)
foodPairings.removeFirst()
print(foodPairings.count)
print(foodPairings.indices)
for i in foodPairings.indices {
let imageView = UIImageView()
let imageStringURL = foodPairings[i].imageURL
let encodedURL = imageStringURL.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed)
guard let postURL: URL = URL(string: encodedURL!) else {return}
imageView.af_setImage(withURL: postURL, placeholderImage: #imageLiteral(resourceName: "Placeholder"), filter: nil, progress: nil, imageTransition: .noTransition, runImageTransitionIfCached: false, completion: nil)
imageView.tag = i
imageView.contentMode = .scaleAspectFill
imageView.isUserInteractionEnabled = true
imageView.layer.cornerRadius = 20.0
imageView.layer.masksToBounds = true
listView.addSubview(imageView)
//attach tap detector
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapImageView)))
}
print(listView.subviews.count)
listView.backgroundColor = UIColor.clear
positionListItems()
}
The print statements in Test result in:
4 3
0..<3 4
Production prints the following:
4 3
0..<3 5
Why is listView.subviews.count different in production?
I have resolved this, but it is not clear what the source of the issue was.
In Test, my Scroll View had one subview even prior to me adding the array of UIImageViews. In Production, the Scroll View had two subviews prior to me adding the images in the array.
Just prior to the loop where I add my subviews, I remove all subviews from the ScrollView:
listView.subviews.forEach({ $0.removeFromSuperview() })
for i in foodPairings.indices {
print("SubviewCount: \(listView.subviews.count)")
let imageView = UIImageView()
...
Both my Test and Production macs are running XCode 9 Swift 4. Still remains a mystery why subview count was different.
Related
Im trying to setup a multiline set of views in a UIScrollView and ultimately a UIStackView. I found out I need to dynamically set the scrollviews height to the content to make it work. So when I went to debug why the values never worked I found that the labelType.frame.size is giving me a massive width or like the text was never wrapped, and the height is the height of the devices view.
If I change around the anchors for the content view I can make the text never line break fueling what I see as the CGSize numbers corresponding to the visual size
While the parent view does not even get a size which is why the default UIScrollView won't even work
content frame (0.0, 0.0)
uitextType (6791.0, 478.0)
labelType (6781.0, 460.5)
In this example I am testing both UITextView and UILabel, I know UITextView is not recommended but testing anyway, it has the same behavior with or without
Worse case I guess I can mix SwiftUI into the Uiview and carry on with work.
override func viewDidLoad() {
super.viewDidLoad()
}
override func loadView() {
super.loadView()
let content = UIView()
content.backgroundColor = UIColor.green
content.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(content)
let constraints2 = [
content.topAnchor.constraint(equalTo: self.view.topAnchor),
content.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
content.heightAnchor.constraint(equalTo: self.view.heightAnchor, constant: 0),
content.widthAnchor.constraint(equalTo: self.view.widthAnchor),
// content.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
// content.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
]
NSLayoutConstraint.activate(constraints2)
let uitextType = UITextView()
uitextType.backgroundColor = .clear
uitextType.text = lotatext
uitextType.isScrollEnabled = false
uitextType.translatesAutoresizingMaskIntoConstraints = false
content.addSubview(uitextType)
uitextType.topAnchor.constraint(equalTo: content.topAnchor).isActive = true
uitextType.leadingAnchor.constraint(equalTo: content.leadingAnchor).isActive = true
uitextType.heightAnchor.constraint(equalTo: content.heightAnchor).isActive = true
uitextType.widthAnchor.constraint(equalTo: content.widthAnchor).isActive = true // -40 2x?! lame
uitextType.font = UIFont.preferredFont(forTextStyle: .body)
uitextType.contentInsetAdjustmentBehavior = .automatic
uitextType.sizeToFit()
uitextType.contentMode = .topLeft
let labelType = UILabel()
labelType.backgroundColor = .clear
labelType.text = lotatext
labelType.numberOfLines = 0
labelType.lineBreakMode = .byWordWrapping
labelType.translatesAutoresizingMaskIntoConstraints = false
content.addSubview(labelType)
labelType.topAnchor.constraint(equalTo: content.topAnchor).isActive = true
labelType.leadingAnchor.constraint(equalTo: content.leadingAnchor).isActive = true
labelType.heightAnchor.constraint(equalTo: content.heightAnchor).isActive = true
labelType.widthAnchor.constraint(equalTo: content.widthAnchor).isActive = true
labelType.font = UIFont.preferredFont(forTextStyle: .body)
labelType.sizeToFit()
print("content frame \(content.frame.size)")
print("uitextType \(uitextType.frame.size)")
print("labelType \(labelType.frame.size)")
}
// testing text
let lotatext = """
Every day is taco ipsum tuesday. It’s a wonderful morning for breakfast tacos. 50 cent tacos! I’ll take 30. CARNE ASADA!! BARBACOA!! I’d have to say, those tacos are on fleek. It’s long been rumored that the chupacabra is really just a crazed man who’s local taco shop went out of business. Does guac cost extra? BARBACOA!! TACOS!! It’s raining tacos, from out of the sky, tacos, don’t even ask why. CARNE ASADA!!
50 cent tacos! I’ll take 30. I’d have to say, those tacos are on fleek. Let’s do a beef and a chicken, and one with both. I’ve been following that taco truck around all day. Make it a double there pal. It’s long been rumored that the chupacabra is really just a crazed man who’s local taco shop went out of business. I think I’ve overdosed on tacos. Tacos Al pastor/De Adobada are made of thin pork steaks seasoned with adobo seasoning, then skewered and overlapped on one another on a vertical rotisserie cooked and flame-broiled as it spins.
... snipped extras paragraphs
"""
Anybody notice that the text in your application's UIPickerViews is rendered incorrectly, with the first character cut off? I'm seeing this in all UIPickerViews in my app, on multiple devices. You can see a few pixels of the first character in most cases.
I've tried deleting derived data, and the application from the phones, but no dice.
I'm not sure which update might have triggered the problem, but it just started in a project that has been stable for months. The code for the labels:
func pickerView(_ pickerView: UIPickerView,
viewForRow row: Int,
forComponent component: Int,
reusing view: UIView?) -> UIView
{
let pickerLabel = UILabel()
pickerLabel.text = "Rec.709"
pickerLabel.font = UIFont(name: "Ropa Sans", size: 18)
pickerLabel.textColor = UIColor.white
pickerLabel.textAlignment = NSTextAlignment.left
}
Did not change anything to my PickerViews, but I am experiencing exactly the same problem since updating to iOS 14. Seems like Apple changed something in the PickerView implementation.
My ViewForRow function is returning a horizontal UIStackView containing three labels. I was able to solve the problem temporarily by adding a 15 points offset constraint to the leading edge of the first label, and the trailing edge of the last:
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
var stackView: UIStackView
if view != nil {
stackView = view as! UIStackView
} else {
let leftLabel = UILabel()
let ctrLabel = UILabel()
let rightLabel = UILabel()
stackView = UIStackView(arrangedSubviews:[leftLabel, ctrLabel, rightLabel])
stackView.axis = .horizontal
stackView.distribution = .fill
// Temporary fix.
leftLabel.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 15.0).isActive = true
rightLabel.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: -15.0).isActive = true
// Set text of labels here...
return stackView
}
I haven't been able to check yet, but I am afraid that this extra margin may now look weird on devices that are still running an older iOS version.
I found only the leftmost component's left-aligned string to be cut off (for example, I have a multipicker view with 3 components, all of which are left-aligned. Only the leftmost component is cutoff.
I found it easier to just modify the strings to pad them with a couple of extra leading spaces - I'm using a dictionary for the strings and so they are all in one place (and used only for display, so I don't have to worry about using values). Worked well in my case. If and when Apple fixes the issue, it will be easy to revert (if necessary).
I'm trying to make a random image appear on the screen, but I'm new to Swift and am not sure how to do this. I have three images which I want have randomly shown in the image view, when the app is opened.
How do I do this?
Generate a ramdom number from 0 to 2 and show the image by randomly generated number.
var random = arc4random_uniform(3) //returns 0 to 2 randomly
switch random {
case 0:
//show first image
case 1:
//show second image
default:
//show third image
}
If the images are named basically the same thing. For example, "Image1.png, Image2.png, and Image3.png, then you can use this code:
override func viewDidLoad() {
super.viewDidLoad()
ImageView.image = UIImage(named: "Image\(arc4random_uniform(3) + 1).png")
}
imageArr = ["1.jpeg","2.jpeg","3.jpeg","4.jpeg"]
let RandomNumber = Int(arc4random_uniform(UInt32(self.imageArr.count)))
//imageArr is array of images
let image = UIImage.init(named: "\(imageArr[RandomNumber])")
let imageView = UIImageView.init(image: image)
It works for me (Swift 4.2):
let images: [UIImage] = [ #imageLiteral(resourceName: "randomImage1"),
#imageLiteral(resourceName: "randomImage2"),
#imageLiteral(resourceName: "randomImage3")]
let randomImage = images.shuffled().randomElement()
We've been tracking an issue on our project where we would have intermittently failing snapshot test cases. The gist of our approach is to render a view controller's view and compare that image to a reference image to see if they're different. There are several layers to our approach here:
FBSnapshotTestCases
Quick
Custom Matchers
Our issue is that sometimes the image created by the rendered view is empty. Just a large (correct size) transparent image.
I've tested each one in isolation and determined that none of those is the problem. Instead, I've been able to reproduce this in a standalone, plain Xcode project.
By using the same approach that FBSnapshotTestCases uses to render a view, I've created a simple test. To reproduce, create a new project of the "Master-Detail" template and give the detail view controller a Storyboard ID of "Detail". Then create this simple unit test.
func testExample1() {
let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let sut = storyboard.instantiateViewControllerWithIdentifier("Detail") as UIViewController
sut.beginAppearanceTransition(true, animated: false)
sut.endAppearanceTransition()
UIGraphicsBeginImageContextWithOptions(sut.view.frame.size, false, 0)
sut.view.drawViewHierarchyInRect(sut.view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let data = UIImagePNGRepresentation(image)
println("byte length: \(data.length)")
}
Nothing too fancy, and it will most likely pass. But, if you duplicate the code a few more times:
func testExample1() { ... }
func testExample2() { ... }
func testExample3() { ... }
The output is very strange (truncated):
Test Suite 'All tests' started at 2014-10-02 07:46:52 +0000
byte length: 27760
byte length: 17645
byte length: 27760
Test Suite 'All tests' passed at 2014-10-02 07:55:29 +0000.
Executed 3 tests, with 0 failures (0 unexpected) in 517.778 (517.781) seconds
The byte lengths should be identical, but they're not. The second test (and sometimes the third) will have an empty view, just like our problem.
A sample project demonstrating the problem is available here.
I was able to reproduce the issue using an Objective-C test project, so it's unlikely that it's a Swift problem. In past projects, we haven't used Storyboards for our view controller UI, so it's possible that there is an extra step necessary in order to "force" the view to load. It's also possible that this is an Xcode 6.x or iOS 8 issue (I've reproduced the problem with Xcode 6.0.1).
Has anyone experienced an issue like this, where rendered images of views from controllers loaded from Storyboards have been transparent?
Seems to do the trick...
let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let sut = storyboard.instantiateViewControllerWithIdentifier("Detail") as UIViewController
sut.beginAppearanceTransition(true, animated: false)
sut.endAppearanceTransition()
UIGraphicsBeginImageContextWithOptions(sut.view.frame.size, false, 0)
let context = UIGraphicsGetCurrentContext
sut.view.layer.renderInContext(context())
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let data = UIImagePNGRepresentation(image)
println("byte length: \(data.length)")
Taking the storyboards out of the equation by switching to a generated UIView:
let view = UIView(frame: CGRectMake(0, 0, 300, 300))
view.backgroundColor = UIColor.blueColor()
UIGraphicsBeginImageContextWithOptions(view.frame.size, false, 0)
view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let data = UIImagePNGRepresentation(image)
println("byte length: \(data.length)")
Gives similar results.
Test Case '-[TestTestTests.TestTestTests testExample1]' started.
byte length: 9663
Test Case '-[TestTestTests.TestTestTests testExample1]' passed (1.000 seconds).
Test Case '-[TestTestTests.TestTestTests testExample2]' started.
byte length: 9663
Test Case '-[TestTestTests.TestTestTests testExample2]' passed (0.112 seconds).
Test Case '-[TestTestTests.TestTestTests testExample3]' started.
byte length: 6469
As this topic suggests you try using "[self.view.layer renderInContext:UIGraphicsGetCurrentContext()]" instead:
drawViewHierarchyInRect:afterScreenUpdates: delays other animations
I'm completely new to coding and I'm trying to learn Swift. I'm trying to cycle through background images for an app. The images I have are named 1.jpg, 2.jpg, 3.jpg, 4.jpg. When I try run the simulator, the "super.viewDidLoad()" line is highlighted green with the comment, "Thread 1, Breakpoint 3.1." Here's my code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var imageList = [UIImage]()
for i in 1...4 {
let imageName = "\(i).jpg"
var image = UIImage(named:imageName)
imageList.append(image)
}
self.myImageView.animationImages = imageList
self.myImageView.animationDuration = 4.0
self.myImageView.startAnimating()
It sounds like you've set a breakpoint there. If there's a blue pointer to the left of the line, click it to disable it. Or, use the keyboard shortcut command-Y to disable all breakpoints.