I have custom table cell. Each cell have different height. I want to give swipe delete button with cell height.
I have used view in cell. View height is cell height - 16. Top-bottom margin 8.
So please help me to do this.
Use this code in your Custom Cell Swift class
override func layoutSubviews() {
let fltHeight:CGFloat = 46
var subviews: [Any] = self.subviews
let subview: UIView? = subviews[0] as? UIView
if NSClassFromString("UITableViewCellDeleteConfirmationView") != nil {
if (subview?.isKind(of: NSClassFromString("UITableViewCellDeleteConfirmationView")!))! {
let deleteButtonView: UIView? = (subview?.subviews[0])
var buttonFrame: CGRect? = deleteButtonView?.frame
buttonFrame?.origin.x = (deleteButtonView?.frame.origin.x)!
buttonFrame?.origin.y = (deleteButtonView?.frame.origin.y)!
buttonFrame?.size.width = (deleteButtonView?.frame.size.width)!
buttonFrame?.size.height = fltHeight
deleteButtonView?.frame = buttonFrame!
// Placing at the center of the cell.
subview?.frame = CGRect(x: CGFloat((subview?.frame.origin.x)!),
y: CGFloat((subview?.frame.origin.y)! + ((subview?.frame.size.height)!-fltHeight)/2),
width: CGFloat((subview?.frame.size.width)!),
height: CGFloat(fltHeight))
deleteButtonView?.clipsToBounds = true
subview?.clipsToBounds = true
}
}
}
Related
any chances to change UISearchBar's UITextField height?
I tried almost all solutions which I've could found, but all are unsuccessful.
Updated:
I realized that ios13 searchBar doesn't contains UITextField, but contains UISearchBarTextField class
and here is hierarchy
as I understood we have to looking for the new class for iOS13 instead of UITextField
I've tried looping and search TextField
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
for subView in searchBar.subviews {
for subsubView in subView.subviews{
for subsubsubView in subsubView.subviews{
if #available(iOS 13.0, *) {
if let textField = subsubsubView as? UISearchTextField{
var bounds: CGRect
bounds = textField.frame
bounds.size.height = 40 //(set height whatever you want)
textField.bounds = bounds
textField.layoutIfNeeded()
}
} else {
if let textField = subsubView as? UITextField {
var bounds: CGRect
bounds = textField.frame
bounds.size.height = 40 //(set height whatever you want)
textField.bounds = bounds
}
}
}
}
}
}
and debugger goes into condition and found UITextField and changed height, but on device - there no height changes
What I finally did was to set an UIImageView as a container for the UISearchBar.
Didn't work with a simple UIView, I guess it had to do with intrinsic content size?
func showSearchBar() {
self.searchNavigationItem?.setRightBarButton(nil, animated: false)
guard let searchBar = self.searchBar else {
assert(false, "Need a search bar to display")
return
}
self.searchContainer = UIImageView()
self.searchContainer?.isUserInteractionEnabled = true
let image = // Some image with the corresponding size.
self.searchContainer?.image = image
self.searchContainer?.alpha = 0
self.searchContainer?.frame = // Same size as the image
// Here you add the searchContainer into the titleView.
self.searchNavigationItem?.titleView = self.searchContainer
searchBar.translatesAutoresizingMaskIntoConstraints = false
// Create top/bottom/leading/trailing constraints
self.searchContainer?.addSubview(searchBar)
searchBar.sizeToFit()
self.searchContainer?.addConstraints([topConstraint, bottomConstraint, leadingConstraint, trailingConstraint])
self?.searchContainer?.alpha = 1
}
I have a custom UITableViewCell that displays a circular image on the left-hand side. Since the default UIImageView supplied with the UITableViewCell is the same height as the row, the images end up nearly touching. I'd like to shrink the image slightly to create some extra padding.
I was able to get this to work using the following code
override func layoutSubviews() {
super.layoutSubviews()
// Make the image view slightly smaller than the row height
self.imageView!.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
// Round corners
self.imageView!.layer.cornerRadius = self.imageView!.bounds.height / 2.0
self.imageView!.layer.borderWidth = 0.5
self.imageView!.layer.borderColor = UIColor.gray.cgColor
self.imageView!.layer.masksToBounds = true
self.imageView!.contentMode = .scaleAspectFill;
self.updateConstraints()
}
override func prepareForReuse() {
super.prepareForReuse()
self.imageView!.image = nil
self.layoutSubviews()
}
This works only for the cells displayed in the table view when it first appears on the screen. Once I scroll (i.e. dequeue a re-usable cell), the transform is no longer applied. The image below shows the left side of the table view. I've captured the region where the original cells transition to re-used cells.
For completeness, here is my tableView(cellForRowAt:) function
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.itemTableView.dequeueReusableCell(withIdentifier: "ItemCell") as! InventoryItemTableViewCell
if let items = self.displayedItems {
if indexPath.row < items.count {
let item = items[indexPath.row]
cell.item = item
cell.textLabel!.text = items[indexPath.row].partNumber
cell.detailTextLabel!.text = items[indexPath.row].description
if let quantity = items[indexPath.row].quantity {
cell.quantityLabel.text = "Qty: \(Int(quantity))"
}
else {
cell.quantityLabel.text = "Qty: N/A"
}
if let stringImageBase64 = item.imageBase64 {
let dataDecoded: Data = Data(base64Encoded: stringImageBase64, options: .ignoreUnknownCharacters)!
cell.imageView!.image = UIImage(data: dataDecoded)
}
else {
cell.imageView!.image = blankImage
}
}
}
return cell
}
I tried other methods such as playing with the image view's insets but this had no effect.
Question
Why is the transform being applied to the original cells when the table is created but not to any re-used cells? Should I be approaching this differently?
I was able to accomplish resizing the image using the following code. It seems the autolayout is applied after the transform, therefore overriding it.
override func layoutSubviews() {
// Layout all subviews except for the image view
super.layoutSubviews()
// Make the image view slightly smaller than the row height
self.imageView!.frame = CGRect(x: self.imageView!.frame.origin.x + 4,
y: self.imageView!.frame.origin.y + 4,
width: 56,
height: 56)
// Round corners
self.imageView!.layer.cornerRadius = self.imageView!.frame.height / 2.0
self.imageView!.layer.borderWidth = 0.5
self.imageView!.layer.borderColor = UIColor.gray.cgColor
self.imageView!.layer.masksToBounds = false
self.imageView!.clipsToBounds = true
self.imageView!.contentMode = .scaleAspectFit;
}
I have a UIViewController that is embedded in a UINavigationController. The navigation item for this view controller is a UISegmentedControl with 3 segments. I'm trying to find a way to add a "new" badge to each of the segments. It appears that UISegmentedControl does not normally allow you to do this but I was thinking that for my purposes, perhaps I could simulate this using a custom UIView positioned at the left or right edge of each segment. I know I can get the width of the UISegmentedControl and since the auto-size mode is set to "equal widths", it seems reasonable that I could simply divide the total width by 3 to determine the approximate width of each segment.
However, there are a couple of things that I'm not sure about:
Is it possible to determine the x/y position of the UISegmentedControl within the navigation bar so I know where to position the custom view(s)?
Is it then possible to add a custom view at these positions inside the space contained by the navigation bar?
Add Views above the Segment Control, put constraint.
In ViewDidLoad() use the code
self.badge1.addSubview(self.addCounter(count: 0))
self.badge2.addSubview(self.addCounter(count: 9))
Use this function to make badge counter
func addCounter(count: Int)->UIView {
// Count > 0, show count
if count > 0 {
// Create label
let fontSize: CGFloat = 10
let label = UILabel()
label.font = UIFont.systemFont(ofSize: fontSize)
label.textAlignment = .center
label.textColor = .white
label.backgroundColor = .red
// Add count to label and size to fit
label.text = "\(NSNumber(value: count))"
label.sizeToFit()
// Adjust frame to be square for single digits or elliptical for numbers > 9
var frame: CGRect = label.frame
frame.size.height += CGFloat(Int(0.4 * fontSize))
frame.size.width = (count <= 9) ? frame.size.height : frame.size.width + CGFloat(Int(fontSize))
label.frame = frame
// Set radius and clip to bounds
label.layer.cornerRadius = frame.size.height / 2.0
label.clipsToBounds = true
// Show label in accessory view and remove disclosure
return label
} else {
return UIView()
}
}
And final result
For those wanting to access the image views via the index, I created an extension, but it's not ideal, as it has to check whether the image data is equal, but works so far for me
extension UISegmentedControl {
func getImageView(at index: Int) -> UIImageView? {
guard let image = imageForSegment(at: index) else { return nil }
let imageViews = subviews.compactMap { $0.subviews.first(where: { $0 is UIImageView }) as? UIImageView }
return imageViews.first(where: {
return $0.image?.isEqualTo(image: image) == true
})
}
}
extension UIImage {
func isEqualTo(image: UIImage) -> Bool {
guard let data1 = image.pngData() else { return false }
guard let data2 = self.pngData() else { return false }
return data1 == data2
}
}
I have a table view cell with a scroll view for scrolling through images. There are a couple of issues I'm having with it.
When the table view is shown, the cell with the scroll view and images shows like the first image and part of the second image and the paging doesn't work and it lets you scroll in any direction.
Once I scroll past this cell and then scroll back to it, it is displayed correctly and paging works like it should.
In cellForRowAtIndexPath()
if indexPath.section == 0 {
let imageCell = tableView.dequeueReusableCellWithIdentifier("imageCell", forIndexPath: indexPath) as! ImageCell
imageCell.selectionStyle = .None
if imageFiles.count > 0 {
if imageFiles.count == 1 {
imageCell.pageControl.hidden = true
}
imageCell.pageControl.numberOfPages = imageFiles.count
imageCell.pageControl.currentPage = 0
var index = 0
for photo in imageFiles {
var frame = CGRect()
frame.origin.x = (imageCell.imageScrollView.frame.size.width * CGFloat(index))
frame.origin.y = 0
frame.size = CGSizeMake(imageCell.imageScrollView.frame.size.width, imageCell.imageScrollView.frame.size.height)
let imageView = UIImageView(frame: frame)
imageView.image = photo
imageCell.imageScrollView.addSubview(imageView)
imageCell.imageScrollView.contentSize = CGSizeMake(imageCell.imageScrollView.frame.size.width * CGFloat(imageFiles.count), imageCell.imageScrollView.frame.size.height)
index += 1
}
} else {
imageCell.pageControl.hidden = true
let imageView = UIImageView(frame: imageCell.imageScrollView.frame)
imageView.image = UIImage(named: "placeholder")
imageCell.imageScrollView.addSubview(imageView)
}
My custom cell:
import UIKit
class ImageCell: UITableViewCell, UIScrollViewDelegate {
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var imageScrollView: UIScrollView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let pageWidth = scrollView.frame.size.width
pageControl.currentPage = Int(scrollView.contentOffset.x / pageWidth)
}
}
I was using SDWebImageDownloader. viewDidLoad() to download images to use in the scroll view but I wasn't reloading the table view data. Once I added that, this works perfectly.
func downloadImages() {
let downloader = SDWebImageDownloader.sharedDownloader()
for imageURL in images {
let url = NSURL(string: imageURL)
downloader.downloadImageWithURL(url, options: SDWebImageDownloaderOptions.UseNSURLCache, progress: nil,
completed: { (image, data, error, bool) -> Void in
self.imageFiles.append(image)
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
})
}
}
You are doing this:
frame.size = CGSizeMake(imageCell.imageScrollView.frame.size.width, imageCell.imageScrollView.frame.size.height)
I would guess that when your view initially loads, imageScrollView has not been fully laid out, so it doesn't have a valid size. Scrolling away from this cell & back to it after the view is fully loaded would result in the scroll view having a valid size.
You should validate that imageScrollView has the width you think it does, and if it doesn't, maybe use the width of the screen instead.
I have implemented a similar thing in one of my apps, so as another possible option, instead of putting a scroll view in your first cell, you could place a CollectionView in there instead and populate your collection view with cells of images. This would A) allow your layout to dynamically change with the view, and B) rather than loading up your scroll view with all the images, they can be dynamically loaded as needed.
I have a table view with custom cell in Swift that contains a horizontal layout UIView. I have noticed that when scrolling the correctly positioned subview in the horizontal view starts to multiply. I guess this has something to do with layoutSubviews() being called when table scrolls and the fact tableview recycles its cells when hidden and shows them when needed, but ignores currently positioned subviews..
It looks something like this
There's already a similar question from before, but it has no good answer.
UIScrollview calling superviews layoutSubviews when scrolling?
Here's the code I'm using inside my custom cell for horizontal positioning:
class HorizontalLayout: UIView {
var xOffsets: [CGFloat] = []
override func layoutSubviews() {
var width: CGFloat = 0
for i in 0..<subviews.count {
var view = subviews[i] as UIView
view.layoutSubviews()
width += xOffsets[i]
view.frame.origin.x = width
width += view.frame.width
}
self.frame.size.width = width
}
override func addSubview(view: UIView) {
xOffsets.append(view.frame.origin.x)
super.addSubview(view)
}
func removeAll() {
for view in subviews {
view.removeFromSuperview()
}
xOffsets.removeAll(keepCapacity: false)
}
}
Taken from here: https://medium.com/swift-programming/dynamic-layouts-in-swift-b56cf8049b08
Using inside custom cell like so:
func loadStops(stops:[String]) {
for stop in stops {
// just testing purposes only
let view = UIView(frame: CGRectMake(10, 0, 40, 40))
view.backgroundColor = UIColor.redColor()
stopsView.addSubview(view)
}
}
Is there a way to fix this problem and prevent the subview of being multiplied when scrolling and perhaps a better way to position subviews horizontally in a tableview cell?