【问题标题】:How to highlight a UIView like Apple does如何像 Apple 一样突出显示 UIView
【发布时间】:2014-02-08 05:40:29
【问题描述】:

当用户触摸 UIButton 时,它会变灰一点。什么苹果能达到这样的效果?当我的自定义 UIButton 突出显示时,我需要它具有相同的效果。

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    AppDelegate* appDelegate=(AppDelegate*)[UIApplication sharedApplication].delegate;
    if ([keyPath isEqualToString:@"highlighted"]){
        UIButton *button = object;
        if (button.isHighlighted) {
            self.backgroundColor=[UIColor colorWithRed:36.0/255.0 green:153.0/255.0 blue:116.0/255.0 alpha:1];
        }else{
            self.backgroundColor=appDelegate.currentAppColor;
        }
    }
}

我正在使用此代码,但更改背景颜色不会影响任何子视图。我也需要它们变灰。

【问题讨论】:

  • 我也在寻找解决方案,但我相当肯定 Apple 正在做一些更复杂的事情。看起来它确定了图像的蒙版并仅使那些区域变暗。不太清楚他们是怎么做到的。
  • 我有简单的解决方案,请参考:stackoverflow.com/a/48161904/6630644

标签: ios uiview uibutton highlight


【解决方案1】:

我认为对此没有简单或明确的答案。

免责声明: 此答案中的所有代码都是从我的脑海中编写的,所以请原谅错误。

我建议这样做:

建议一:降低所有视图子视图的不透明度,这不会影响颜色...

目标 C:

-(void)buttonTouched{
    for(UIView *subview in self.subviews){ 
        subview.alpha = 0.5;
    }
}

斯威夫特:

func buttonTouched() {
    for subview in subviews {
        subview.alpha = 0.5
    }
}

建议 2(未测试):尝试通过手动操作来覆盖所有子视图(缺点:您还必须手动设置颜色,如果是这样,那将是疯狂的不是单色的):

目标 C:

-(void)buttonTouched{
    for(UIView *subview in self.subviews){

        if([subview respondsToSelector:@selector(setBackgroundColor:)]){ 
            //for generic views - changes UILabel's backgroundColor too, though
            subview.backgroundColor = [UIColor grayColor];
        }

        if([subview respondsToSelector:@selector(setTextColor:)]){ 
            //reverse effect of upper if statement(if needed)
            subview.backgroundColor = [UIColor clearColor];
            subview.textColor = [UIColor grayColor];
        }
    }
}

斯威夫特:

func buttonTouched() {
    for subview in subviews {
        if subview.responds(to: #selector(setter: AVMutableVideoCompositionInstruction.backgroundColor)) {
            //for generic views - changes UILabel's backgroundColor too, though
            subview.backgroundColor = UIColor.gray
        }

        if subview.responds(to: #selector(setter: UILabel.textColor)) {
            //reverse effect of upper if statement(if needed)
            subview.backgroundColor = UIColor.clear
            subview.textColor = UIColor.gray
        }
    }
}

这是一个非常糟糕的设计,可能会导致很多问题,但它可能会对您有所帮助。上面的例子需要很多改进,我只是想给你一个提示。您必须恢复 touchesEnded: 方法中的颜色。也许对你有帮助……

建议 3:用透明视图(如果是矩形)覆盖整个视图

目标 C:

-(void)buttonTouched{
    UIView *overlay = [[UIView alloc]initWithFrame:self.bounds];
    overlay.backgroundColor = [[UIColor grayColor]colorWithAlphaComponent:0.5];
    [self addSubview: overlay];
}

斯威夫特:

func buttonTouched() {
    let overlay = UIView(frame: bounds)
    overlay.backgroundColor = UIColor.gray.withAlphaComponent(0.5)
    addSubview(overlay)
}

当然,当用户松开手指时,您必须将其移除。

建议 4:另一种选择是创建当前视图的位图并根据自己的喜好修改像素,这是一项相当多的工作,因此我将在此处省略代码。

Apple 可能会混合使用后两者。当一个按钮被触摸时,它将越过像素并在每个具有 alpha 分量的像素上覆盖一个灰色像素。

我希望我能帮上忙。

【讨论】:

  • 我想到了你的方法3,最后还是用了它。
  • 很高兴我能帮上忙 :-)
【解决方案2】:

结束了这段代码

maskView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
maskView.backgroundColor=[[UIColor blackColor]colorWithAlphaComponent:0.5];

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
        if ([keyPath isEqualToString:@"highlighted"]){
            UIButton *button = object;
            if (button.isHighlighted) {
                [self addSubview:maskView];
            }else{
                [maskView removeFromSuperview];
            }
        }
    }

【讨论】:

    【解决方案3】:
    1. 为你的 UIView 创建一个点击手势

      UITapGestureRecognizer* buttonViewTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(categoryButtonAction:)];
      
    2. 现在将以下定义添加到您的代码中。它会在视图中产生一些相似的效果。

      -(void)touchGestureAction:(UIGestureRecognizer*)gesture
      {
          UIView * sender =  gesture.view;
      
          UIColor *color = sender.backgroundColor;
          sender.backgroundColor = [UIColor whiteColor];
          sender.alpha = 0.7;
      
          dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC);
      //  This will create flash effect
          dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
              sender.backgroundColor = color;
              sender.alpha = 1.0;
          });
      }
      

    这将创建一些与按钮触摸效果非常相似的效果。

    【讨论】:

      【解决方案4】:

      我知道这是一个老问题。但是,如果有人仍在寻找通用的触摸高亮解决方案,我创建了一个小类别来解决这个问题。

      https://github.com/mta452/UIView-TouchHighlighting

      用法:

      只需在视图控制器中导入类别并在 viewDidLoad 中添加以下行。

      buttonView.touchHighlightingStyle = MTHighlightingStyleHollowDarkOverlay;
      

      不同的突出显示样式:

      【讨论】:

      • 您的分类看起来不错,但实际上它适用于所有 UIView。在应用程序的所有视图中都替换了有关触摸事件的方法。
      • 是的,这是通过方法调配完成的,因此所有视图都会受到影响。但这没什么大不了的,因为这些方法仅在触摸发生时才调用,因此不会影响性能。其发展背后的基本思想是将现有观点转换为突出观点。可能有一个 UIView 的子类,但它会对现有的代码库产生很大影响。
      • 如果我错了,请纠正我。方法 swizzling 是在 load 方法中创建的。它会从一开始就被调用(不是你说的“只有在触摸发生时才调用)。如果你将断点放在方法swizzling中,你会看到。关于子类化,你可能是对的。但是,必须实现子类化对于所有 UIControl(例如,UILabel、UIButton、UICollectionView 等)
      • 你说得对,方法调配是在加载方法中创建的。但是这些 swizzled 方法仅在发生触摸时才被调用。处理高亮状态后,调用原始实现。它的工作方式类似于具有重写方法的子类。
      • 这是一种向任何元素添加类似按钮的行为的快速方法,尽管为整个视图控制器执行此操作感觉很奇怪,如果我只想在类中使用它,我该怎么办继承自 UILabel?
      【解决方案5】:

      斯威夫特 3:

      在您的自定义 UIView 类中覆盖 touchesBegantouchesEnded 回调

      override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
          self.layer.backgroundColor = UIColor.white.cgColor
      }
      
      override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
          self.layer.backgroundColor = UIColor.gray.cgColor
      }
      

      或者更好地使用阴影而不是改变背景

      CardView 代码,除了从https://github.com/aclissold/CardView 借来的 touches 事件

      @IBDesignable
      class CardView: UIView {
      
          @IBInspectable var cornerRadius: CGFloat = 5
      
          @IBInspectable var shadowOffsetWidth: Int = 0
          @IBInspectable var shadowOffsetHeight: Int = 1
          @IBInspectable var shadowColor: UIColor? = UIColor.black
          @IBInspectable var shadowOpacity: Float = 0.23
      
          override func layoutSubviews() {
              layer.cornerRadius = cornerRadius
              let shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
      
              layer.masksToBounds = false
              layer.shadowColor = shadowColor?.cgColor
              layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight);
              layer.shadowOpacity = shadowOpacity
              layer.shadowPath = shadowPath.cgPath
          }
      
          override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
              layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight * 2);
      
          }
      
          override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
              layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: 
      
          shadowOffsetHeight);
              }
      
          }
      

      更新:

      我还添加了touchesCancelled 回调以防止我的UIView 卡在选定状态。

       override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
              layer.shadowOffset = CGSize(width: shadowOffsetWidth, height: shadowOffsetHeight);
          } 
      

      【讨论】:

        【解决方案6】:

        想法:

        1. 添加要触发触摸区域的按钮。

        2. 使用事件:Touch Down、Touch Up Inside 和 Touch Up Outside

        3. 在Touch Down设置Highlight状态值,其他设置Normal vale。

        您可以使用情节提要 IBAction 来做,或以编程方式。

        示例代码(以编程方式):

        - (void)viewDidLoad {
            [super viewDidLoad];
        
            firstView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
            firstView.backgroundColor = [UIColor blueColor];
        
            firstBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
            [firstBtn addTarget:self action:@selector(touchDownCollection) forControlEvents:UIControlEventTouchDown];
            [firstBtn addTarget:self action:@selector(touchUpInSideCollection) forControlEvents:UIControlEventTouchUpInside];
            [firstBtn addTarget:self action:@selector(touchUpOutSideCollection) forControlEvents:UIControlEventTouchUpOutside];
        
            [self.view addSubview:firstView];
            [self.view addSubview:firstBtn];
        
            //Need add: firstView.userInteractionEnabled = NO; if
            //[self.view addSubview:firstBtn];
            //[self.view addSubview:firstView];
        }
        
        - (void)touchDownCollection {
            // Highlight state value: alpha, background...
            firstView.alpha = 0.5;
        }
        
        - (void)touchUpInSideCollection {
            // Normal state value.
            firstView.alpha = 1.0;
        }
        
        - (void)touchUpOutSideCollection {
            // Normal state value.
            firstView.alpha = 1.0;
        }
        

        【讨论】:

          【解决方案7】:

          您可以使用自定义视图,例如:

          class HighlightView: UIView {
          
              override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
                  DispatchQueue.main.async {
                      self.alpha = 1.0
                      UIView.animate(withDuration: 0.4, delay: 0.0, options: .curveLinear, animations: {
                          self.alpha = 0.5
                      }, completion: nil)
                  }
              }
          
              override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
                  DispatchQueue.main.async {
                      self.alpha = 0.5
                      UIView.animate(withDuration: 0.4, delay: 0.0, options: .curveLinear, animations: {
                          self.alpha = 1.0
                      }, completion: nil)
                  }
              }
          
              override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
                  DispatchQueue.main.async {
                      self.alpha = 0.5
                      UIView.animate(withDuration: 0.4, delay: 0.0, options: .curveLinear, animations: {
                          self.alpha = 1.0
                      }, completion: nil)
                  }
              }
          }
          

          然后你可以使用它来代替UIView,当你点击它时,它会改变它的alpha值,所以它看起来像一个亮点。

          【讨论】:

          • 记得在重写这些方法时调用超级实现,如documentation 所述。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-14
          相关资源
          最近更新 更多